eda, compiler, documentor: Proper prototypes for invoking virtual methods directly
[sdk] / documentor / src / Documentor.ec
1 import "ecere"
2 import "ec"
3 import "HTMLView"
4 import "IDESettings"
5 import "SettingsDialog"
6
7 static Context globalContext { };
8 static OldList defines { };
9 static OldList imports { };
10 static NameSpace globalData;
11 static OldList excludedSymbols { offset = (uint)&((Symbol)0).left }; 
12
13 #define UTF8_NUM_BYTES(x)  (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
14
15 default:
16 /*extern */int __ecereVMethodID_class_OnGetString;
17 private:
18
19
20 static void Dummy()
21 {
22 int a;
23 a.OnGetString(null, null, null);
24 }
25
26 static bool editing = true;
27
28 enum CodeObjectType { typeClass, typeData, typeMethod, typeEvent, typeProperty, typeNameSpace, typeDataType, typeEnumValue, typeDataPrivate, typeMethodPrivate, typePropertyPrivate };
29
30 static char * iconNames[CodeObjectType] = 
31 {
32    "<:ecere>constructs/class.png",
33    "<:ecere>constructs/data.png",
34    "<:ecere>constructs/method.png",
35    "<:ecere>constructs/event.png",
36    "<:ecere>constructs/property.png",
37    "<:ecere>constructs/namespace.png",
38    "<:ecere>constructs/dataType.png",
39    "<:ecere>constructs/enumValue.png",
40    "<:ecere>constructs/dataPrivate.png",
41    "<:ecere>constructs/methodPrivate.png",
42    "<:ecere>constructs/propertyPrivate.png"
43 };
44
45 IDESettings settings { }; // instantiate the IDESettings class from the IDESettings.ec file. Do this at a global level so that all methods can access settings.
46
47 IDESettingsContainer settingsContainer
48 {
49    driver = "JSON";
50    data = settings;
51    dataOwner = &settings;
52 };
53
54 // WARNING : This function expects a null terminated string since it recursively concatenate...
55 static void _PrintType(Type type, char * string, bool printName, bool printFunction, bool fullName)
56 {
57    if(type)
58    {
59       switch(type.kind)
60       {
61          case classType:
62             if(type._class && type._class.string)
63             {
64                if(fullName)
65                   strcat(string, type._class.string);
66                else
67                {
68                   if(type._class.registered)
69                   {
70                      char hex[10];
71                      sprintf(hex, "%08x", type._class.registered);
72                      strcat(string, "<a href=\"api://");
73                      strcat(string, hex);
74                      strcat(string, "\" style=\"text-decoration: none;\">");
75                      strcat(string, type._class.registered.name);
76                      strcat(string, "</a>");
77                   }
78                   else
79                      strcat(string, type._class.string);                     
80                }
81             }
82             break;
83          case pointerType:
84          {
85             /*Type funcType;
86             for(funcType = type; funcType && (funcType.kind == pointerType || funcType.kind == arrayType); funcType = funcType.type);
87             if(funcType && funcType.kind == functionType)
88             {
89                Type param;
90                DocPrintType(funcType.returnType, string, false, fullName);
91                strcat(string, "(*");
92                if(printName || funcType.thisClass)
93                {
94                   strcat(string, " ");
95                   if(funcType.thisClass)
96                   {
97                      strcat(string, funcType.thisClass.string);
98                      strcat(string, "::");
99                   }
100                   if(type.name)
101                      strcat(string, type.name);
102                }
103                strcat(string, ")(");
104                for(param = funcType.params.first; param; param = param.next)
105                {
106                   DocPrintType(param, string, false, fullName);
107                   if(param.next) strcat(string, ", ");
108                }
109                strcat(string, ")");               
110             }
111             else*/
112             {
113                _PrintType(type.type, string, false /*printName*/, printFunction, fullName);
114                if(string[strlen(string)-1] == '(')
115                   strcat(string, "*");
116                else
117                   strcat(string, " *");
118             }
119             break;
120          }
121          case voidType: strcat(string, "void"); break;
122          case intType:  strcat(string, type.isSigned ? "int" : "uint"); break;
123          case int64Type:  strcat(string, type.isSigned ? "int64" : "uint64"); break;
124          case charType: strcat(string, type.isSigned ? "char" : "byte"); break;
125          case shortType: strcat(string, type.isSigned ? "short" : "uint16"); break;
126          case floatType: strcat(string, "float"); break;
127          case doubleType: strcat(string, "double"); break;
128          case structType:
129             if(type.enumName)
130             {
131                strcat(string, "struct ");
132                strcat(string, type.enumName);
133             }
134             else if(type.typeName)
135             {
136                strcat(string, type.typeName);
137             }
138             else
139             {
140                /*
141                strcat(string, "struct ");
142                strcat(string,"(unnamed)");
143                */
144                Type member;
145                strcat(string, "struct {");
146                for(member = type.members.first; member; member = member.next)
147                {
148                   DocPrintType(member, string, true, fullName);
149                   strcat(string,"; ");
150                }
151                strcat(string,"}");
152             }
153             break;
154          case unionType:
155             if(type.enumName)
156             {
157                strcat(string, "union ");
158                strcat(string, type.enumName);
159             }
160             else if(type.typeName)
161             {
162                strcat(string, type.typeName);
163             }
164             else
165             {
166                strcat(string, "union ");
167                strcat(string,"(unnamed)");
168             }
169             break;
170          case enumType:
171             if(type.enumName)
172             {
173                strcat(string, "enum ");
174                strcat(string, type.enumName);
175             }
176             else if(type.typeName)
177             {
178                strcat(string, type.typeName);
179             }
180             else
181                strcat(string, "enum");
182             break;
183          case functionType:
184          {
185             if(printFunction)
186             {
187                if(type.dllExport)
188                   strcat(string, "dllexport ");
189                DocPrintType(type.returnType, string, false, fullName);
190                strcat(string, " ");
191             }
192             
193             // DANGER: Testing This
194             if(printName)
195             {
196                if(type.name)
197                {
198                   if(fullName)
199                      strcat(string, type.name);
200                   else
201                   {
202                      char * name = RSearchString(type.name, "::", strlen(type.name), true, false);
203                      if(name) name += 2; else name = type.name;
204                      strcat(string, "<b>");
205                      strcat(string, name);
206                      strcat(string, "</b>");
207                   }
208                }
209                else
210                {
211                   printf("");
212                }
213             }
214
215             if(printFunction)
216             {
217                Type param;
218                strcat(string, "(");
219                for(param = type.params.first; param; param = param.next)
220                {
221                   DocPrintType(param, string, true, fullName);
222                   if(param.next) strcat(string, ", ");
223                }
224                strcat(string, ")");
225             }
226             break;
227          }
228          case arrayType:
229          {
230             /*Type funcType;
231             for(funcType = type; funcType && (funcType.kind == pointerType || funcType.kind == arrayType); funcType = funcType.type);
232             if(funcType && funcType.kind == functionType)
233             {
234                Type param;
235                DocPrintType(funcType.returnType, string, false, fullName);
236                strcat(string, "(*");
237                if(printName || funcType.thisClass)
238                {
239                   strcat(string, " ");
240                   if(funcType.thisClass)
241                   {
242                      strcat(string, funcType.thisClass.string);
243                      strcat(string, "::");
244                   }
245                   if(type.name)
246                      strcat(string, type.name);
247                }
248                strcat(string, ")(");
249                for(param = funcType.params.first; param; param = param.next)
250                {
251                   DocPrintType(param, string, false, fullName);
252                   if(param.next) strcat(string, ", ");
253                }
254                strcat(string, ")");               
255             }
256             else*/
257             {
258                char baseType[1024], size[256];
259                Type arrayType = type;
260                baseType[0] = '\0';
261                size[0] = '\0';
262
263                while(arrayType.kind == TypeKind::arrayType)
264                {
265                   strcat(size, "[");
266                   if(arrayType.enumClass)
267                      strcat(size, arrayType.enumClass.string);
268                   else if(arrayType.arraySizeExp)
269                      PrintExpression(arrayType.arraySizeExp, size);
270                   //sprintf(string, "%s[%s]", baseType, size); 
271                   strcat(size, "]");
272
273                   arrayType = arrayType.arrayType;
274                }
275                _PrintType(arrayType, baseType, printName, printFunction, fullName);
276                strcat(string, baseType);
277                strcat(string, size);
278             }
279
280             /*
281                DocPrintType(type.arrayType, baseType, printName, fullName);
282                if(type.enumClass)
283                   strcpy(size, type.enumClass.string);
284                else if(type.arraySizeExp)
285                   PrintExpression(type.arraySizeExp, size);
286                //sprintf(string, "%s[%s]", baseType, size); 
287                strcat(string, baseType);
288                strcat(string, "[");
289                strcat(string, size); 
290                strcat(string, "]");
291                */
292
293             printName = false;
294             break;
295          }
296          case ellipsisType:
297             strcat(string, "...");
298             break;
299          case methodType:
300             _PrintType(type.method.dataType, string, false, printFunction, fullName);
301             break;
302          case subClassType:
303             strcat(string, "subclass(");
304             strcat(string, type._class ? type._class.string : "int");
305             strcat(string, ")");                  
306             break;
307          default:
308             printf("");
309       }
310       if(type.name && printName && type.kind != functionType && (type.kind != pointerType || type.type.kind != functionType))
311       {
312          strcat(string, " ");
313          strcat(string, type.name);
314       }
315    }
316 }
317
318 void DocPrintType(Type type, char * string, bool printName, bool fullName)
319 {
320    Type funcType;
321    for(funcType = type; funcType && (funcType.kind == pointerType || funcType.kind == arrayType); funcType = funcType.type);
322    if(funcType && funcType.kind == functionType && type != funcType)
323    {
324       char typeString[1024];
325       Type param;
326
327       DocPrintType(funcType.returnType, string, false, fullName);
328       strcat(string, "(");
329       _PrintType(type, string, printName, false, fullName);
330       strcat(string, ")");
331       /*
332       if(type.name)
333          strcat(string, type.name);
334       else
335       {
336          printf("");
337       }
338       */
339       strcat(string, "(");
340       for(param = funcType.params.first; param; param = param.next)
341       {
342          DocPrintType(param, string, true, fullName);
343          if(param.next) strcat(string, ", ");
344       }
345       strcat(string, ")");
346    }
347    else
348       _PrintType(type, string, printName, true, fullName);
349 }
350
351 void AddComponents(Module module, bool isDll)
352 {
353    DataRow row = null;
354    SubModule m;
355
356    if(module.name && (!strcmp(module.name, "ecere") || !strcmp(module.name, "ecereCOM")))
357    {
358       row = mainForm.browser.AddRow();
359       row.SetData(null, APIPageNameSpace { name = "ecereCOM", nameSpace = &module.application.systemNameSpace });
360       row.tag = (int)null;
361       AddNameSpace(row, null, module.application.systemNameSpace, null, "", !isDll);
362    }
363
364    for(m = module.modules.first; m; m = m.next)
365    {
366       if(m.importMode == publicAccess || !isDll)
367          AddComponents(m.module, true);
368    }
369
370    // PUT MODULE DESCRIPTION HERE
371    if(module.name && strcmp(module.name, "ecereCOM"))
372    {
373       row = mainForm.browser.AddRow();
374       row.SetData(null, APIPageNameSpace { name = module.name, module = module, nameSpace = &module.publicNameSpace });
375       row.tag = (int)module;
376       AddNameSpace(row, module, module.publicNameSpace, null /*module.application.systemNameSpace*/, "", !isDll);
377       if(!isDll)
378          AddNameSpace(row, module, module.privateNameSpace, null /*module.application.systemNameSpace*/, "", !isDll);
379    }
380 }
381
382 class APIPage
383 {
384 public:
385    char * name;
386    APIPage page;
387    char * label;
388    bool showPrivate;
389
390    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
391    {
392       return name;
393    }
394
395    virtual void Generate(File f)
396    {
397       page.Generate(f);
398    }
399 };
400
401 enum DocumentationType
402 {
403    nameSpaceDoc,
404    classDoc,
405    functionDoc,
406    methodDoc
407 };
408
409 enum DocumentationItem
410 {
411    description,
412    usage,
413    remarks,
414    example,
415    seeAlso,
416    enumerationValue,
417    definition,
418    conversion,
419    memberDescription,
420    propertyDescription,
421    parameter,
422    returnValue
423 };
424
425 static void FigureFileName(char * fileName, Module module, DocumentationType type, void * object, DocumentationItem item, void * data)
426 {
427    NameSpace * nameSpace, * ns;
428    Class cl = null;
429    Method method = null;
430    GlobalFunction function = null;
431    char nsName[1024], temp[1024];
432    char docFile[1024];
433
434
435    switch(type)
436    {
437       case nameSpaceDoc: nameSpace = object; break;
438       case classDoc:     cl = (Class)object; nameSpace = cl.nameSpace; break;
439       case functionDoc:  function = object; nameSpace = function.nameSpace; break;
440       case methodDoc:    method = object; cl = method._class; nameSpace = cl.nameSpace; break;
441    }
442
443    nsName[0] = 0;
444    ns = nameSpace;
445    while(ns && ns->name)
446    {
447       strcpy(temp, "namespaces/");
448       strcat(temp, ns->name);
449       strcat(temp, "/");
450       strcat(temp, nsName);
451       strcpy(nsName, temp);
452       ns = ns->parent;
453    }
454    sprintf(docFile, "%s.eCdoc", (!module || !module.name || !strcmp(nsName, "namespaces/ecere/namespaces/com")) ? "ecereCOM" : module.name);
455
456    sprintf(fileName, "<%s/%s>", settings.docDir, docFile); // Note that in the ecereIDE.ini file, there can be no quotes around the path, and there needs to be the final backslash. Otherwise this does not work.
457    strcat(fileName, nsName);
458
459    if(cl)
460    {
461       strcat(fileName, "classes/");
462       strcat(fileName, cl.name);
463       strcat(fileName, "/");
464    }
465
466    if(method)
467    {
468       strcat(fileName, "methods/");
469       strcat(fileName, method.name);
470       strcat(fileName, "/");
471    }
472    else if(function)
473    {
474       char * name = RSearchString(function.name, "::", strlen(function.name), true, false);
475       if(name) name += 2; else name = function.name;
476       strcat(fileName, "functions/");
477       strcat(fileName, name);
478       strcat(fileName, "/");
479    }
480
481    switch(item)
482    {
483       case description: strcat(fileName, "description"); break;
484       case usage: strcat(fileName, "usage"); break;
485       case remarks: strcat(fileName, "remarks"); break;
486       case example: strcat(fileName, "example"); break;
487       case seeAlso: strcat(fileName, "seeAlso"); break;
488       case returnValue: strcat(fileName, "returnValue"); break;
489       case enumerationValue: 
490          strcat(fileName, "enumeration values/");
491          strcat(fileName, ((NamedLink)data).name);
492          break;
493       case definition:
494          strcat(fileName, "definitions/");
495          strcat(fileName, ((Definition)data).name);
496          break;
497       case conversion:
498       {
499          char * name = RSearchString(((Property)data).name, "::", strlen(((Property)data).name), true, false);
500          if(name) name += 2; else name = ((Property)data).name;
501          strcat(fileName, "conversions/");
502          strcat(fileName, name);
503          break;
504       }
505       case memberDescription:
506          strcat(fileName, "data members/");
507          strcat(fileName, ((DataMember)data).name);
508          break;
509       case propertyDescription:
510          strcat(fileName, "properties/");
511          strcat(fileName, ((Property)data).name);
512          break;
513       case parameter:
514       {
515          int count;
516          char name[1024];
517          Type prev;
518          strcat(fileName, "parameters/");
519          for(prev = data, count = 0; prev; prev = prev.prev, count++);
520          sprintf(name, "%s.%d", ((Type)data).name, count);
521          strcat(fileName, name);
522          break;
523       }
524    }
525 }
526
527 static char * ReadDoc(Module module, DocumentationType type, void * object, DocumentationItem item, void * data)
528 {
529    char fileName[MAX_LOCATION];
530    String contents = null;
531    File file;
532
533    FigureFileName(fileName, module, type, object, item, data);
534    file = FileOpen(fileName, read);
535    if(file)
536    {
537       uint len;
538       if((len = file.GetSize()))
539       {
540          contents = new char[len+1];
541          file.Read(contents, 1, len);
542          contents[len] = '\0';      
543       }
544       delete file;
545    }
546    if(contents)
547    {
548       int c;
549       for(c = 0; contents[c]; c++)
550          if(!isspace(contents[c])) break;
551       if(!contents[c])
552          delete contents;      
553    }
554    if(editing && !contents)
555       contents = CopyString($"[Add Text]");
556    return contents;
557 }
558
559 class APIPageNameSpace : APIPage
560 {
561    NameSpace * nameSpace;
562    Module module;
563    
564    void Generate(File f)
565    {
566       char string[1024];
567       char nsName[1024], temp[1024];
568       NameSpace * ns;
569       BTNamedLink link;
570       uint tag;
571
572       nsName[0] = 0;
573       ns = nameSpace;
574       while(ns && ns->name)
575       {
576          strcpy(temp, ns->name);
577          if(nsName[0]) strcat(temp, "::");
578          strcat(temp, nsName);
579          strcpy(nsName, temp);
580          ns = ns->parent;
581       }
582       // Generate Class Page
583       f.Printf($"<HTML><HEAD><TITLE>API Reference</TITLE></HEAD>\n<BODY><FONT SIZE=\"3\">\n");
584       if(nsName[0])
585       {
586          f.Printf("<FONT FACE=\"Arial\" SIZE=\"6\">%s</FONT><br><br>\n", nsName );
587          tag = (uint)nameSpace;
588          f.Printf($"Module: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
589       }
590       else
591       {
592          tag = (uint)((!module || !module.name || !strcmp(nsName, "ecere::com") ? null : module));
593          f.Printf($"<FONT FACE=\"Arial\" SIZE=\"6\">Module %s</FONT><br>\n", (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
594       }
595
596       nsName[0] = 0;
597       ns = nameSpace->parent;
598       while(ns && ns->name)
599       {
600          strcpy(temp, ns->name);
601          if(nsName[0]) strcat(temp, "::");
602          strcat(temp, nsName);
603          strcpy(nsName, temp);
604          ns = ns->parent;
605       }
606       if(nsName[0]) 
607          f.Printf($"Parent namespace: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", nameSpace->parent, nsName);
608
609       f.Printf("<br>");
610       {
611          char * desc = ReadDoc(module, nameSpaceDoc, nameSpace, description, null);
612          if(desc)
613          {
614             f.Printf($"<H3>Description</H3><br><br>\n");
615             if(editing)
616             {
617                char fileName[MAX_LOCATION];
618                FigureFileName(fileName, module, nameSpaceDoc, nameSpace, description, null);
619                f.Printf("<a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
620                f.Puts(desc);
621                f.Printf("</a><br><br>");
622             }
623             else
624                f.Printf("%s<br><br>", desc);
625             delete desc;
626          }
627       }
628
629       if(nameSpace->nameSpaces.first)
630       {
631          bool first = true;
632          for(ns = (NameSpace *)nameSpace->nameSpaces.first; ns; ns = (NameSpace *)((BTNode)ns).next)
633          {
634             char * desc = ReadDoc(module, nameSpaceDoc, ns, description, null);
635             if(first)
636             {
637                f.Printf($"<H3>Sub Namespaces</H3><br><br>\n");
638                f.Printf("<TABLE >\n");
639                first = false;
640             }
641             f.Printf("<TR>");
642             f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeNameSpace], ns, ns->name);
643             if(desc)
644             {
645                if(editing)
646                {
647                   char fileName[MAX_LOCATION];
648                   FigureFileName(fileName, module, nameSpaceDoc, ns, description, null);
649                   f.Printf("<TD valign=top height=22> <a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
650                   f.Puts(desc);
651                   f.Printf("</a></TD>");
652                }
653                else
654                   f.Printf("<TD valign=top height=22> %s</TD>", desc);
655                delete desc;
656             }
657             f.Printf("</TR><br>\n");
658          }
659          if(!first)
660             f.Printf("</TABLE><br>\n");
661       }
662
663       if(nameSpace->classes.first)
664       {
665          bool first = true;
666          for(link = (BTNamedLink)nameSpace->classes.first; link; link = (BTNamedLink)((BTNode)link).next)
667          {
668             Class cl = link.data;
669             if(!cl.templateClass)
670             {
671                char * desc = ReadDoc(module, classDoc, cl, description, null);
672
673                if(first)
674                {
675                   f.Printf($"<a name=Classes></a><H3>Classes</H3><br><br>\n");
676                   f.Printf("<TABLE >\n");
677                   first = false;
678                }
679
680                f.Printf("<TR>");
681
682                f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", (cl.type == enumClass || cl.type == unitClass || cl.type == systemClass) ? iconNames[typeDataType] : iconNames[typeClass], cl, cl.name);
683                if(desc)
684                {
685                   if(editing)
686                   {
687                      char fileName[MAX_LOCATION];
688                      FigureFileName(fileName, module, classDoc, cl, description, null);
689                      f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
690                      f.Puts(desc);
691                      f.Printf("</a></TD>");
692                   }
693                   else
694                      f.Printf("<TD valign=top height=22>%s</TD>", desc);
695                   delete desc;
696                }            
697                f.Printf("</TR>\n");
698             }
699          }
700          if(!first)
701             f.Printf("</TABLE><br>\n");
702       }
703
704       if(nameSpace->functions.first)
705       {
706          bool first = true;
707          for(link = (BTNamedLink)nameSpace->functions.first; link; link = (BTNamedLink)((BTNode)link).next)
708          {
709             GlobalFunction function = link.data;
710             char * desc = ReadDoc(module, functionDoc, function, description, null);
711             char * name = RSearchString(function.name, "::", strlen(function.name), true, false);
712             if(name) name += 2; else name = function.name;
713             if(first)
714             {
715                f.Printf($"<a name=Functions></a><H3>Functions</H3><br><br>\n");
716                f.Printf("<TABLE >\n");
717                first = false;
718             }
719             f.Printf("<TR>");
720             f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeMethod], function, name);
721             if(desc)
722             {
723                if(editing)
724                {
725                   char fileName[MAX_LOCATION];
726                   FigureFileName(fileName, module, functionDoc, function, description, null);
727                   f.Printf("<TD valign=top height=22> <a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
728                   f.Puts(desc);
729                   f.Printf("</a></TD>");
730                }
731                else
732                   f.Printf("<TD valign=top height=22> %s</TD>", desc);
733                delete desc;
734             }
735             f.Printf("</TR><br>\n");
736          }
737          if(!first)
738             f.Printf("</TABLE><br>\n");
739       }
740
741       if(nameSpace->defines.first)
742       {
743          bool first = true;
744          for(link = (BTNamedLink)nameSpace->defines.first; link; link = (BTNamedLink)((BTNode)link).next)
745          {
746             DefinedExpression def = link.data;
747             char * desc = ReadDoc(module, nameSpaceDoc, nameSpace, definition, def);
748             if(first)
749             {
750                f.Printf($"<a name=Definitions></a><H3>Definitions</H3><br><br>\n");
751                f.Printf("<TABLE >\n");
752                first = false;
753             }
754             f.Printf("<TR>");
755             f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", def, iconNames[typeData], def.name);
756             f.Printf("<TD valign=top height=22>%s</TD>", def.value);
757             if(desc)
758             {
759                if(editing)
760                {
761                   char fileName[MAX_LOCATION];
762                   FigureFileName(fileName, module, nameSpaceDoc, nameSpace, definition, def);
763                   f.Printf("<TD valign=top height=22> <a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
764                   f.Puts(desc);
765                   f.Printf("</a></TD>");
766                }
767                else
768                   f.Printf("<TD valign=top height=22> %s</TD>", desc);
769                delete desc;
770             }
771             f.Printf("</TR><br>\n");
772          }
773          if(!first)
774             f.Printf("</TABLE><br>\n");
775       }
776
777       f.Printf("</FONT></BODY></HTML>\n");
778    }   
779 }
780
781 class APIPageClass : APIPage
782 {
783    Class cl;
784
785    void Generate(File f)
786    {
787       char string[1024];
788       Method method;
789       Property prop;
790       DataMember member;
791       char nsName[1024], temp[1024];
792       NameSpace * ns = cl.nameSpace;
793       Module module = cl.module;
794
795       nsName[0] = 0;
796       while(ns && ns->name)
797       {
798          strcpy(temp, ns->name);
799          if(nsName[0]) strcat(temp, "::");
800          strcat(temp, nsName);
801          strcpy(nsName, temp);
802          ns = ns->parent;
803       }
804       // Generate Class Page
805       f.Printf($"<HTML><HEAD><TITLE>API Reference</TITLE></HEAD>\n<BODY><FONT SIZE=\"3\">\n");
806       f.Printf("<FONT FACE=\"Arial\" SIZE=\"6\">%s</FONT><br><br>\n", name);
807
808       f.Printf($"Module: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
809       if(nsName[0]) 
810          f.Printf($"Namespace: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", cl.nameSpace, nsName);
811
812       {
813          char * classType = null;
814          switch(cl.type)
815          {
816             case bitClass:
817                classType = $"Bit Collection";
818                break;
819             case enumClass:
820                classType = $"Enumeration";
821                break;
822             case structClass:
823                classType = $"Structure";
824                break;
825             case normalClass:
826                classType = $"Class";
827                break;
828             case noHeadClass:
829                classType = $"Class (No header)";
830                break;
831             case unitClass:
832                classType = $"Unit";
833                break;
834             case systemClass:
835                classType = $"Basic Data Type";
836                break;
837          }
838          f.Printf($"Type: %s<br>\n", classType);
839       }
840       
841       if(cl.type != systemClass && cl.base)
842       {
843          f.Printf($"Base Class: ");
844          if(!strcmp(cl.base.name, "struct") || !strcmp(cl.base.name, "class"))
845          {
846             f.Printf(cl.type == bitClass ? cl.dataTypeString : $"None");
847          }
848          else if(cl.type == enumClass && !strcmp(cl.base.name, "enum"))
849             f.Printf("%s", cl.dataTypeString);
850          else
851             f.Printf("<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a>", cl.base, cl.base.name);
852          f.Printf("<br>\n");
853       }
854
855       {
856          char * desc = ReadDoc(module, classDoc, cl, description, null);
857          if(desc)
858          {
859             f.Printf($"<br><H3>Description</H3><br><br>\n");
860             if(editing)
861             {
862                char fileName[MAX_LOCATION];
863                FigureFileName(fileName, module, classDoc, cl, description, null);
864                f.Printf("<a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
865                f.Puts(desc);
866                f.Printf("</a><br><br>");
867             }
868             else
869                f.Printf("%s<br><br>", desc);
870             delete desc;
871          }
872       }
873
874       if(cl.type == enumClass)
875       {
876          EnumClassData enumeration = (EnumClassData)cl.data;
877          if(enumeration.values.first)
878          {
879             NamedLink item;
880                         
881             f.Printf($"<a name=EnumerationValues></a><H3>Enumeration Values</H3><br><br>\n");
882             f.Printf("<TABLE >\n");
883
884             for(item = enumeration.values.first; item; item = item.next)
885             {
886                char * desc = ReadDoc(module, classDoc, cl, enumerationValue, item);
887                bool needClass = true;
888                Class dataClass;
889                Class base = cl;
890                char tempString[1024];
891                String s;
892                while(base.type == enumClass) base = base.base;
893
894                if(base.type == systemClass ||
895                   (base.type == bitClass && base.membersAndProperties.first && !strcmp(cl.fullName, ((DataMember)base.membersAndProperties.first).dataTypeString)))
896                {
897                   if(!base.dataType)
898                      base.dataType = ProcessTypeString(base.dataTypeString, false);
899
900                   if(base.dataType.kind != classType)
901                   {
902                      char string[256];
903                      Symbol classSym;
904                      string[0] = '\0';
905                      PrintType(base.dataType, string, false, true);
906                      classSym = FindClass(string);
907                      dataClass = classSym ? classSym.registered : null;
908                   }
909                   else
910                      dataClass = base.dataType._class ? base.dataType._class.registered : null;
911                }
912                else
913                   dataClass = base;                  
914                
915                f.Printf("<TR>");
916                f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", item, iconNames[typeEnumValue], item.name);
917                if(dataClass.type == systemClass)
918                {
919                   needClass = false;
920                   s = ((char *(*)(void *, void *, char *, void *, bool *))(void *)dataClass._vTbl[__ecereVMethodID_class_OnGetString])(dataClass, &item.data, tempString, null, &needClass);
921                }
922                else
923                   s = ((char *(*)(void *, void *, char *, void *, bool *))(void *)eSystem_FindClass(componentsApp, "class")._vTbl[__ecereVMethodID_class_OnGetString])(dataClass, &item.data, tempString, null, &needClass);
924                if(needClass)
925                   f.Printf("<TD valign=top height=22 nowrap=1>%s { %s }</TD>", dataClass.name, s);
926                else
927                   f.Printf("<TD valign=top height=22 nowrap=1>%s</TD>", s);
928                if(desc)
929                {
930                   if(editing)
931                   {
932                      char fileName[MAX_LOCATION];
933                      FigureFileName(fileName, module, classDoc, cl, enumerationValue, item);
934                      f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
935                      f.Puts(desc);
936                      f.Printf("</a></TD>");
937                   }
938                   else
939                      f.Printf("<TD valign=top height=22>%s</TD>", desc);
940                   delete desc;
941                }
942                f.Printf("</TR>");
943             }
944             f.Printf("</TABLE><BR>\n");
945          }
946       }
947
948       if(cl.conversions.first)
949       {
950          f.Printf($"<a name=Conversions></a><H3>Conversions</H3><br><br>\n");
951          f.Printf("<TABLE >\n");
952          for(prop = cl.conversions.first; prop; prop = prop.next)
953          {
954             if((prop.memberAccess == publicAccess || (prop.memberAccess == privateAccess && showPrivate)) && prop.name)
955             {
956                char * desc = ReadDoc(module, classDoc, cl, conversion, prop);
957                DataRow mRow;
958                char * name;
959                Type type = ProcessTypeString(prop.name, false);
960                name = RSearchString(prop.name, "::", strlen(prop.name), true, false);
961                if(name) name += 2; else name = prop.name;
962
963                f.Printf("<TR>");
964                
965                string[0] = 0;
966                DocPrintType(type, string, true, false);
967                
968                f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", prop, iconNames[typeDataType], string);
969                if(desc)
970                {
971                   if(editing)
972                   {
973                      char fileName[MAX_LOCATION];
974                      FigureFileName(fileName, module, classDoc, cl, conversion, prop);
975                      f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
976                      f.Puts(desc);
977                      f.Printf("</a></TD>");
978                   }
979                   else
980                      f.Printf("<TD valign=top height=22>%s</TD>", desc);
981                   delete desc;
982                }
983                
984                f.Printf("</TR>\n");
985                
986                FreeType(type);
987             }
988          }
989          f.Printf("</TABLE><br>\n");
990       }
991
992       if(cl.membersAndProperties.first)
993       {
994          bool first = true;
995          for(prop = (Property)cl.membersAndProperties.first; prop; prop = prop.next)
996          {
997             if(prop.memberAccess == publicAccess || (prop.memberAccess == privateAccess && showPrivate))
998             {
999                if(first)
1000                {
1001                   f.Printf($"<a name=Members></a><H3>Properties and Members</H3><br><br>\n");
1002                   f.Printf("<TABLE >\n");
1003                   first = false;
1004                }
1005
1006                if(prop.isProperty)
1007                {
1008                   char * desc = ReadDoc(module, classDoc, cl, propertyDescription, prop);
1009                   if(!prop.dataType)
1010                      prop.dataType = ProcessTypeString(prop.dataTypeString, false);
1011
1012                   f.Printf("<TR>");
1013                   string[0] = 0;
1014                   DocPrintType(prop.dataType, string, true, false);
1015
1016                   f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", prop, iconNames[typeProperty], prop.name);
1017                   f.Printf("<TD valign=top height=22 nowrap=1>%s</TD>", string);
1018                   if(desc)
1019                   {
1020                      if(editing)
1021                      {
1022                         char fileName[MAX_LOCATION];
1023                         FigureFileName(fileName, module, classDoc, cl, propertyDescription, prop);
1024                         f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1025                         f.Puts(desc);
1026                         f.Printf("</a></TD>");
1027                      }
1028                      else
1029                         f.Printf("<TD valign=top height=22>%s</TD>", desc);
1030                      delete desc;
1031                   }
1032                   f.Printf("</TR>\n");
1033                }
1034                else
1035                {
1036                   AddDataMemberToPage(f, (DataMember)prop, 0, showPrivate);
1037                }
1038             }
1039          }
1040          if(!first)
1041             f.Printf("</TABLE><br>\n");
1042       }
1043
1044       if(cl.methods.first)
1045       {
1046          bool first = true;
1047          // Virtual Methods
1048          for(method = (Method)cl.methods.first; method; method = (Method)((BTNode)method).next)
1049          {
1050             if((method.memberAccess == publicAccess || (method.memberAccess == privateAccess && showPrivate)) && method.type == virtualMethod)
1051             {
1052                char * desc = ReadDoc(module, methodDoc, method, description, null);
1053                if(first)
1054                {
1055                   f.Printf($"<a name=VirtualMethods></a><H3>Virtual Methods</H3><br><br>\n");
1056                   f.Printf("<TABLE >\n");
1057                   first = false;
1058                }
1059                if(!method.dataType)
1060                   ProcessMethodType(method);
1061
1062                f.Printf("<TR>");
1063                f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", method.dataType.thisClass ? iconNames[typeEvent] : iconNames[typeMethod], method, method.name);
1064                if(desc)
1065                {
1066                   if(editing)
1067                   {
1068                      char fileName[MAX_LOCATION];
1069                      FigureFileName(fileName, module, methodDoc, method, description, null);
1070                      f.Printf("<TD valign=top height=22> <a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1071                      f.Puts(desc);
1072                      f.Printf("</a></TD>");
1073                   }
1074                   else
1075                      f.Printf("<TD valign=top height=22> %s</TD>", desc);
1076                   delete desc;
1077                }
1078                f.Printf("</TR><br>\n");
1079             }
1080          }
1081          if(!first)
1082             f.Printf("</TABLE><br>\n");
1083
1084          // Non-Virtual Methods
1085          first = true;
1086          for(method = (Method)cl.methods.first; method; method = (Method)((BTNode)method).next)
1087          {
1088             if((method.memberAccess == publicAccess || (method.memberAccess == privateAccess && showPrivate)) && method.type != virtualMethod)
1089             {
1090                char * desc = ReadDoc(module, methodDoc, method, description, null);
1091                if(first)
1092                {
1093                   f.Printf($"<a name=Methods></a><H3>Non-Virtual Methods</H3><br><br>\n");
1094                   f.Printf("<TABLE >\n");
1095                   first = false;
1096                }
1097
1098                if(!method.dataType)
1099                   ProcessMethodType(method);
1100
1101                f.Printf("<TR>");
1102                f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeMethod], method, method.name);
1103                if(desc)
1104                {
1105                   if(editing)
1106                   {
1107                      char fileName[MAX_LOCATION];
1108                      FigureFileName(fileName, module, methodDoc, method, description, null);
1109                      f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1110                      f.Puts(desc);
1111                      f.Printf("</a></TD>");
1112                   }
1113                   else
1114                      f.Printf("<TD valign=top height=22>%s</TD>", desc);
1115                   delete desc;
1116                }
1117                
1118                f.Printf("</TR><br>\n");
1119             }
1120          }
1121          if(!first)
1122             f.Printf("</TABLE><br>\n");
1123       }
1124       {
1125          char * usageDoc = ReadDoc(module, classDoc, cl, usage, null);
1126          if(usageDoc)
1127          {
1128             f.Printf($"<H3>Usage</H3><br>\n");
1129             if(editing)
1130             {
1131                char fileName[MAX_LOCATION];
1132                FigureFileName(fileName, module, classDoc, cl, usage, null);
1133                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1134                f.Puts(usageDoc);
1135                f.Printf("</a>\n");
1136             }
1137             else
1138                f.Printf("<br>%s\n", usageDoc);
1139             f.Printf("<br><br>\n");
1140             delete usageDoc;
1141          }
1142       }
1143       {
1144          char * exampleDoc = ReadDoc(module, classDoc, cl, example, null);
1145          if(exampleDoc)
1146          {
1147             f.Printf($"<H3>Example</H3><br>\n");
1148             f.Printf($"<FONT face=\"Courier New\">\n");
1149             f.Printf("<br><TABLE >\n");
1150             if(editing)
1151             {
1152                char fileName[MAX_LOCATION];
1153                FigureFileName(fileName, module, classDoc, cl, example, null);
1154                f.Printf("<TR><TD><CODE><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1155                f.Puts(exampleDoc);
1156                f.Printf("</a></CODE></TD></TR>\n"); // bgcolor=#CFC9C0
1157             }
1158             else
1159                f.Printf("<TR><TD><CODE>%s</CODE></TD></TR>\n", exampleDoc);   // bgcolor=#CFC9C0
1160
1161             f.Printf("</TABLE></FONT>\n");
1162             f.Printf("<br>\n");
1163             delete exampleDoc;
1164          }
1165       }
1166       {
1167          char * remarksDoc = ReadDoc(module, classDoc, cl, remarks, null);
1168
1169          if(remarksDoc)
1170          {
1171             f.Printf($"<H3>Remarks</H3><br>\n");
1172             if(editing)
1173             {
1174                char fileName[MAX_LOCATION];
1175                FigureFileName(fileName, module, classDoc, cl, remarks, null);
1176                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1177                f.Puts(remarksDoc);
1178                f.Printf("</a>\n");
1179             }
1180             else
1181                f.Printf("<br>%s\n", remarksDoc);
1182             f.Printf("<br><br>\n");
1183             delete remarksDoc;
1184          }
1185       }
1186       
1187       if(cl.type != systemClass)
1188       {
1189          bool first = true;
1190          OldLink c;
1191          for(c = cl.derivatives.first; c; c = c.next)
1192          {
1193             Class deriv = c.data;
1194             // TO VERIFY: Does this properly check public status?
1195             if(eSystem_FindClass(componentsApp, deriv.fullName))
1196             {
1197                if(first)
1198                {
1199                   f.Printf($"<H3>Derived Classes</H3><br>\n");
1200                   f.Printf("<br>");
1201                   first = false;
1202                }
1203                else
1204                   f.Printf(", ");
1205                f.Printf("<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a>", deriv, deriv.name);
1206              }            
1207          }
1208          if(!first)
1209             f.Printf("<br><br>\n");
1210       }
1211       {
1212          char * seeAlsoDoc = ReadDoc(module, classDoc, cl, seeAlso, null);
1213          if(seeAlsoDoc)
1214          {
1215             f.Printf($"<H3>See Also</H3><br>\n");
1216             if(editing)
1217             {
1218                char fileName[MAX_LOCATION];
1219                FigureFileName(fileName, module, classDoc, cl, seeAlso, null);
1220                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1221                f.Puts(seeAlsoDoc);
1222                f.Printf("</a>\n");
1223             }
1224             else
1225                f.Printf("<br>%s\n", seeAlsoDoc);
1226             f.Printf("<br><br>\n");
1227             delete seeAlsoDoc;
1228          }
1229       }
1230       f.Printf("</FONT></BODY></HTML>\n");
1231    }   
1232 }
1233
1234 class APIPageMethod : APIPage
1235 {
1236    Method method;
1237    void Generate(File f)
1238    {
1239       Class cl = method._class;
1240       char string[1024];
1241       Module module = cl.module;
1242       Type param;
1243       char nsName[1024], temp[1024];
1244       NameSpace * ns = cl.nameSpace;
1245
1246       nsName[0] = 0;
1247       while(ns && ns->name)
1248       {
1249          strcpy(temp, ns->name);
1250          if(nsName[0]) strcat(temp, "::");
1251          strcat(temp, nsName);
1252          strcpy(nsName, temp);
1253          ns = ns->parent;
1254       }
1255
1256       f.Printf($"<HTML><HEAD><TITLE>API Reference</TITLE></HEAD>\n<BODY><FONT SIZE=\"3\">\n");
1257       f.Printf("<FONT FACE=\"Arial\" SIZE=\"6\">%s</FONT><br><br>\n", name);
1258
1259       f.Printf($"Module: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
1260       if(nsName[0])
1261          f.Printf($"Namespace: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", cl.nameSpace, nsName);
1262       f.Printf("Class: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", cl, cl.name);
1263       if(method.dataType.staticMethod)
1264       {
1265          f.Printf($"this pointer class: None<br>\n");
1266       }
1267       else if(method.dataType.thisClass && method.dataType.thisClass.registered && (method.dataType.thisClass.registered != method._class || method.type == virtualMethod))
1268       {
1269          f.Printf($"this pointer class: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", method.dataType.thisClass.registered, method.dataType.thisClass.registered.name);
1270       }
1271
1272       // Generate Method Page
1273       string[0] = 0;
1274       if(!method.dataType.name)
1275          method.dataType.name = CopyString(method.name);
1276       DocPrintType(method.dataType, string, true, false);
1277       f.Printf("<br>%s", string);
1278
1279       {
1280          char * desc = ReadDoc(module, methodDoc, method, description, null);
1281          if(desc)
1282          {
1283             f.Printf($"<br><br><H3>Description</H3><br><br>\n");
1284             if(editing)
1285             {
1286                char fileName[MAX_LOCATION];
1287                FigureFileName(fileName, module, methodDoc, method, description, null);
1288                f.Printf("<a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1289                f.Puts(desc);
1290                f.Printf("</a>");
1291             }
1292             else
1293                f.Printf("%s", desc);
1294             delete desc;
1295          }
1296       }
1297
1298       f.Printf("<br><br>\n");
1299       if(method.dataType.params.first && ((Type)method.dataType.params.first).kind != voidType)
1300       {
1301          f.Printf($"<H3>Parameters</H3><br><br>\n");
1302       }
1303       if((method.dataType.returnType && method.dataType.returnType.kind != voidType) ||
1304          (method.dataType.params.first && ((Type)method.dataType.params.first).kind != voidType))
1305       {
1306          f.Printf("<TABLE  valign=center>\n");
1307       }
1308
1309       for(param = method.dataType.params.first; param; param = param.next)
1310       {
1311          // ADD DESCRIPTION HERE
1312          if(param.kind != voidType)
1313          {
1314             char * desc = ReadDoc(module, methodDoc, method, parameter, param);
1315             f.Printf("<TR>");
1316             string[0] = 0;
1317             DocPrintType(param, string, false, false);
1318
1319             f.Printf("<TD valign=top height=22 nowrap=1>%s&nbsp;</TD>\n", param.name ? param.name : "");
1320             f.Printf("<TD valign=top height=22 nowrap=1>%s&nbsp;</TD>\n", string);
1321             if(desc)
1322             {
1323                if(editing)
1324                {
1325                   char fileName[MAX_LOCATION];
1326                   FigureFileName(fileName, module, methodDoc, method, parameter, param);
1327                   f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1328                   f.Puts(desc);
1329                   f.Printf("s</a>&nbsp;</TD>\n");
1330                }
1331                else
1332                   f.Printf("<TD valign=top height=22>%s&nbsp;</TD>\n", desc);
1333                delete desc;
1334             }
1335             
1336             f.Printf("</TR>\n");
1337          }
1338       }
1339       if(method.dataType.returnType && method.dataType.returnType.kind != voidType)
1340       {
1341          char * desc = ReadDoc(module, methodDoc, method, returnValue, null);
1342          if(method.dataType.params.first && ((Type)method.dataType.params.first).kind != voidType)
1343          {
1344             f.Printf("<TR><TD>&nbsp;</TD></TR>");
1345          }
1346          f.Printf("<TR>");
1347          f.Printf($"<TD valign=top height=22 nowrap=1><B>Return Value</B></TD>\n");
1348          string[0] = 0;
1349          DocPrintType(method.dataType.returnType, string, false, false);
1350          f.Printf("<TD valign=top height=22>%s&nbsp;</TD>\n", string);
1351          if(desc)
1352          {
1353             if(editing)
1354             {
1355                char fileName[MAX_LOCATION];
1356                FigureFileName(fileName, module, methodDoc, method, returnValue, null);
1357                f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1358                f.Puts(desc);
1359                f.Printf("</a>&nbsp;</TD>\n");
1360             }
1361             else
1362                f.Printf("<TD valign=top height=22>%s&nbsp;</TD>\n", desc);
1363             delete desc;
1364          }
1365          f.Printf("</TR>\n");
1366          f.Printf("</TABLE>\n");
1367       }
1368       if((method.dataType.returnType && method.dataType.returnType.kind != voidType) ||
1369          (method.dataType.params.first && ((Type)method.dataType.params.first).kind != voidType))
1370       {
1371          f.Printf("</TABLE><br>\n");
1372       }
1373       {
1374          char * usageDoc = ReadDoc(module, methodDoc, method, usage, null);
1375          if(usageDoc)
1376          {
1377             f.Printf($"<H3>Usage</H3><br>\n");
1378             if(editing)
1379             {
1380                char fileName[MAX_LOCATION];
1381                FigureFileName(fileName, module, methodDoc, method, usage, null);
1382                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1383                f.Puts(usageDoc);
1384                f.Printf("</a>\n");
1385             }
1386             else
1387                f.Printf("<br>%s\n", usageDoc);
1388             f.Printf("<br><br>\n");
1389             delete usageDoc;
1390          }
1391       }
1392       {
1393          char * exampleDoc = ReadDoc(module, methodDoc, method, example, null);
1394          if(exampleDoc)
1395          {
1396             f.Printf($"<H3>Example</H3><br>\n");
1397             f.Printf($"<FONT face=\"Courier New\">\n");
1398             f.Printf("<br><TABLE >\n");
1399             if(editing)
1400             {
1401                char fileName[MAX_LOCATION];
1402                FigureFileName(fileName, module, methodDoc, method, example, null);
1403                f.Printf("<TR><TD><CODE><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1404                f.Puts(exampleDoc);
1405                f.Printf("</a></CODE></TD></TR>\n");   // bgcolor=#CFC9C0
1406             }
1407             else
1408                f.Printf("<TR><TD><CODE>%s</CODE></TD></TR>\n", exampleDoc);   // bgcolor=#CFC9C0
1409             f.Printf("</TABLE></FONT>\n");
1410             f.Printf("<br>\n");
1411             delete exampleDoc;
1412          }
1413       }
1414       {
1415          char * remarksDoc = ReadDoc(module, methodDoc, method, remarks, null);
1416          if(remarksDoc)
1417          {
1418             f.Printf($"<H3>Remarks</H3><br>\n");
1419             if(editing)
1420             {
1421                char fileName[MAX_LOCATION];
1422                FigureFileName(fileName, module, methodDoc, method, remarks, null);
1423                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1424                f.Puts(remarksDoc);
1425                f.Printf("</a>\n");
1426             }
1427             else
1428                f.Printf("<br>%s\n", method, remarksDoc);
1429             f.Printf("<br><br>\n");
1430             delete remarksDoc;
1431          }
1432       }
1433       {
1434          char * seeAlsoDoc = ReadDoc(module, methodDoc, method, seeAlso, null);
1435          if(seeAlsoDoc)
1436          {
1437             f.Printf($"<H3>See Also</H3><br>\n");
1438             if(editing)
1439             {
1440                char fileName[MAX_LOCATION];
1441                FigureFileName(fileName, module, methodDoc, method, seeAlso, null);
1442                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1443                f.Puts(seeAlsoDoc);
1444                f.Printf("</a>\n");
1445             }
1446             else
1447                f.Printf("<br>%s\n", method, seeAlsoDoc);
1448             
1449             f.Printf("<br><br>\n");
1450             delete seeAlsoDoc;
1451          }
1452       }
1453       f.Printf("</FONT></BODY></HTML>\n");
1454    }   
1455 }
1456
1457 class APIPageFunction : APIPage
1458 {
1459    GlobalFunction function;
1460    void Generate(File f)
1461    {
1462       char string[1024];
1463       Module module = function.module;
1464       Type param;
1465       char nsName[1024], temp[1024];
1466       NameSpace * ns = function.nameSpace;
1467
1468       nsName[0] = 0;
1469       while(ns && ns->name)
1470       {
1471          strcpy(temp, ns->name);
1472          if(nsName[0]) strcat(temp, "::");
1473          strcat(temp, nsName);
1474          strcpy(nsName, temp);
1475          ns = ns->parent;
1476       }
1477
1478       f.Printf($"<HTML><HEAD><TITLE>API Reference</TITLE></HEAD>\n<BODY><FONT SIZE=\"3\">\n");
1479       f.Printf("<FONT FACE=\"Arial\" SIZE=\"6\">%s</FONT><br><br>\n", name);
1480
1481       f.Printf($"Module: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
1482
1483       if(nsName[0])
1484          f.Printf($"Namespace: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", function.nameSpace, nsName);
1485
1486       if(!function.dataType)
1487          function.dataType = ProcessTypeString(function.dataTypeString, false);
1488
1489       if(function.dataType.thisClass && function.dataType.thisClass.registered)
1490       {
1491          f.Printf($"this pointer class: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", function.dataType.thisClass.registered, function.dataType.thisClass.registered.name);
1492       }
1493
1494       // Generate Method Page
1495       string[0] = 0;
1496       if(!function.dataType.name)
1497          function.dataType.name = CopyString(function.name);
1498       DocPrintType(function.dataType, string, true, false);
1499       f.Printf("<br>%s", string);
1500
1501       {
1502          char * desc = ReadDoc(module, functionDoc, function, description, null);
1503          if(desc)
1504          {
1505             f.Printf($"<br><br><H3>Description</H3><br><br>\n");
1506             if(editing)
1507             {
1508                char fileName[MAX_LOCATION];
1509                FigureFileName(fileName, module, functionDoc, function, description, null);
1510                f.Printf("<a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1511                f.Puts(desc);
1512                f.Printf("</a>");
1513             }
1514             else
1515                f.Printf("%s", desc);
1516             delete desc;
1517          }
1518       }
1519       f.Printf("<br><br>\n");
1520       if(function.dataType.params.first && ((Type)function.dataType.params.first).kind != voidType)
1521       {
1522          f.Printf($"<H3>Parameters</H3><br><br>\n");
1523       }
1524       if((function.dataType.returnType && function.dataType.returnType.kind != voidType) ||
1525          (function.dataType.params.first && ((Type)function.dataType.params.first).kind != voidType))
1526       {
1527          f.Printf("<TABLE  valign=center>\n");
1528       }
1529
1530       for(param = function.dataType.params.first; param; param = param.next)
1531       {
1532          // ADD DESCRIPTION HERE
1533          if(param.kind != voidType)
1534          {
1535             char * desc = ReadDoc(module, functionDoc, function, parameter, param);
1536             f.Printf("<TR>");
1537             string[0] = 0;
1538             DocPrintType(param, string, false, false);
1539
1540             f.Printf("<TD valign=top height=22 nowrap=1>%s&nbsp;</TD>\n", param.name ? param.name : "");
1541             f.Printf("<TD valign=top height=22 nowrap=1>%s&nbsp;</TD>\n", string);
1542             if(param)
1543             {
1544                if(editing)
1545                {
1546                   char fileName[MAX_LOCATION];
1547                   FigureFileName(fileName, module, functionDoc, function, parameter, param);
1548                   f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1549                   f.Puts(desc);
1550                   f.Printf("</a>&nbsp;</TD>\n");
1551                }
1552                else
1553                   f.Printf("<TD valign=top height=22>%s&nbsp;</TD>\n", desc);
1554                delete desc;
1555             }
1556             f.Printf("</TR>\n");
1557          }
1558       }
1559       if(function.dataType.returnType && function.dataType.returnType.kind != voidType)
1560       {
1561          char * desc = ReadDoc(module, functionDoc, function, returnValue, null);
1562          if(function.dataType.params.first && ((Type)function.dataType.params.first).kind != voidType)
1563          {
1564             f.Printf("<TR><TD>&nbsp;</TD></TR>");
1565          }
1566          f.Printf("<TR>");
1567          f.Printf($"<TD valign=top height=22 nowrap=1><B>Return Value</B></TD>\n");
1568          string[0] = 0;
1569          DocPrintType(function.dataType.returnType, string, false, false);
1570          f.Printf("<TD valign=top height=22>%s&nbsp;</TD>\n", string);
1571          if(desc)
1572          {
1573             if(editing)
1574             {
1575                char fileName[MAX_LOCATION];
1576                FigureFileName(fileName, module, functionDoc, function, returnValue, null);
1577                f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1578                f.Puts(desc);
1579                f.Printf("</a>&nbsp;</TD>\n");
1580             }
1581             else
1582                f.Printf("<TD valign=top height=22>%s&nbsp;</TD>\n", function, desc);
1583             delete desc;
1584          }
1585          f.Printf("</TR>\n");
1586          f.Printf("</TABLE>\n");
1587       }
1588       if((function.dataType.returnType && function.dataType.returnType.kind != voidType) ||
1589          (function.dataType.params.first && ((Type)function.dataType.params.first).kind != voidType))
1590       {
1591          f.Printf("</TABLE><br>\n");
1592       }
1593       {
1594          char * usageDoc = ReadDoc(module, functionDoc, function, usage, null);
1595          if(usageDoc)
1596          {
1597             f.Printf($"<H3>Usage</H3><br>\n");
1598             if(editing)
1599             {
1600                char fileName[MAX_LOCATION];
1601                FigureFileName(fileName, module, functionDoc, function, usage, null);
1602                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1603                f.Puts(usageDoc);
1604                f.Printf("</a>\n");
1605             }
1606             else
1607                f.Printf("<br>%s\n", usageDoc);
1608             f.Printf("<br><br>\n");
1609             delete usageDoc;
1610          }
1611       }
1612       {
1613          char * exampleDoc = ReadDoc(module, functionDoc, function, example, null);
1614          if(exampleDoc)
1615          {
1616             f.Printf($"<H3>Example</H3><br>\n");
1617             f.Printf($"<FONT face=\"Courier New\">\n");
1618             f.Printf("<br><TABLE >\n");
1619             if(editing)
1620             {
1621                char fileName[MAX_LOCATION];
1622                FigureFileName(fileName, module, functionDoc, function, example, null);
1623                f.Printf("<TR><TD><CODE><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1624                f.Puts(exampleDoc);
1625                f.Printf("</a></CODE></TD></TR>\n");   // bgcolor=#CFC9C0
1626             }
1627             else
1628                f.Printf("<TR><TD><CODE>%s</CODE></TD></TR>\n", exampleDoc);   // bgcolor=#CFC9C0
1629             f.Printf("</TABLE></FONT>\n");
1630             f.Printf("<br>\n");
1631             delete exampleDoc;
1632          }
1633       }
1634       {
1635          char * remarksDoc = ReadDoc(module, functionDoc, function, remarks, null);
1636          if(remarksDoc)
1637          {
1638             f.Printf($"<H3>Remarks</H3><br>\n");
1639             if(editing)
1640             {
1641                char fileName[MAX_LOCATION];
1642                FigureFileName(fileName, module, functionDoc, function, remarks, null);
1643                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1644                f.Puts(remarksDoc);
1645                f.Printf("</a>\n");
1646             }
1647             else
1648                f.Printf("<br>%s\n", remarksDoc);
1649             f.Printf("<br><br>\n");
1650             delete remarksDoc;
1651          }
1652       }
1653       {
1654          char * seeAlsoDoc = ReadDoc(module, functionDoc, function, seeAlso, null);
1655          if(seeAlsoDoc)
1656          {
1657             f.Printf($"<H3>See Also</H3><br>\n");
1658             if(editing)
1659             {
1660                char fileName[MAX_LOCATION];
1661                FigureFileName(fileName, module, functionDoc, function, seeAlso, null);
1662                f.Printf("<br><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1663                f.Puts(seeAlsoDoc);
1664                f.Printf("</a>\n");
1665             }
1666             else
1667                f.Printf("<br>%s\n", seeAlsoDoc);
1668             f.Printf("<br><br>\n");
1669             delete seeAlsoDoc;
1670          }
1671       }
1672       f.Printf("</FONT></BODY></HTML>\n");
1673    }   
1674 }
1675
1676 static void AddNameSpace(DataRow parentRow, Module module, NameSpace mainNameSpace, NameSpace comNameSpace, char * parentName, bool showPrivate)
1677 {
1678    char nsName[1024];
1679    NameSpace * ns;
1680    NameSpace * nameSpace = mainNameSpace;
1681    DataRow row;
1682    DataRow classesRow = null;
1683    DataRow functionsRow = null, definesRow = null;
1684    APIPage page;
1685
1686    char fileName[MAX_LOCATION];
1687    
1688    strcpy(nsName, parentName ? parentName : "");
1689    if(nameSpace->name)
1690    {
1691       if(nsName[0])
1692          strcat(nsName, "::");
1693       strcat(nsName, nameSpace->name);
1694    }
1695
1696    if(nsName[0])
1697    {
1698       row = parentRow.AddRow();
1699       row.SetData(null, (page = APIPageNameSpace { nameSpace->name, module = module, nameSpace = nameSpace, showPrivate = showPrivate }));
1700       row.tag = (int)nameSpace;
1701       row.icon = mainForm.icons[typeNameSpace];
1702    }
1703    else
1704    {
1705       // "Global NameSpace"
1706       row = parentRow;
1707       page = parentRow.GetData(null);
1708    }
1709
1710    {
1711       bool first = true;
1712
1713       for(ns = (NameSpace *)mainNameSpace.nameSpaces.first; ns; ns = (NameSpace *)((BTNode)ns).next)
1714       {
1715          NameSpace * comNS = (comNameSpace != null) ? (NameSpace *)comNameSpace.nameSpaces.FindString(ns->name) : null;
1716          AddNameSpace(row, module, ns, comNS, nsName, showPrivate);
1717       }
1718       if(comNameSpace != null)
1719       {
1720          for(ns = (NameSpace *)comNameSpace.nameSpaces.first; ns; ns = (NameSpace *)((BTNode)ns).next)
1721          {
1722             if(!mainNameSpace.nameSpaces.FindString(ns->name))
1723             {
1724                AddNameSpace(row, module, ns, null, nsName, showPrivate);
1725             }
1726          }
1727       }
1728    }
1729    if(mainNameSpace.classes.first || (comNameSpace && comNameSpace.classes.first))
1730    {
1731       for(nameSpace = mainNameSpace ; nameSpace; nameSpace = (nameSpace == mainNameSpace) ? comNameSpace : null)
1732       {
1733          if(nameSpace->classes.first)
1734          {
1735             BTNamedLink link;
1736             Class cl;
1737             for(link = (BTNamedLink)nameSpace->classes.first; link; link = (BTNamedLink)((BTNode)link).next)
1738             {
1739                cl = link.data;
1740                if(!cl.templateClass && (!module || cl.module == module || (!cl.module.name && !strcmp(module.name, "ecere"))))
1741                {
1742                   if(!classesRow) { classesRow = row.AddRow(); classesRow.SetData(null, APIPage { $"Classes", page = page }); classesRow.collapsed = true; classesRow.icon = mainForm.icons[typeClass]; classesRow.tag = 1; }
1743                   AddClass(classesRow, module, cl, nsName, showPrivate);
1744                }
1745             }
1746          }
1747       }
1748    }
1749
1750    if(mainNameSpace.functions.first || (comNameSpace && comNameSpace.functions.first))
1751    {
1752       for(nameSpace = mainNameSpace ; nameSpace; nameSpace = (nameSpace == mainNameSpace) ? comNameSpace : null)
1753       {
1754          if(nameSpace->functions.first)                            
1755          {
1756             BTNamedLink link;
1757             GlobalFunction fn;
1758             for(link = (BTNamedLink)nameSpace->functions.first; link; link = (BTNamedLink)((BTNode)link).next)
1759             {
1760                fn = link.data;
1761                if(!module || fn.module == module || (!fn.module.name && !strcmp(module.name, "ecere")))
1762                {
1763                   char * name = ( name = RSearchString(fn.name, "::", strlen(fn.name), false, false), name ? name + 2 : fn.name);
1764                   DataRow fnRow;
1765                   if(!functionsRow) { functionsRow = row.AddRow(); functionsRow.SetData(null, APIPage { $"Functions", page = page }); functionsRow.collapsed = true; functionsRow.icon = mainForm.icons[typeMethod];  functionsRow.tag = 2; };
1766                   fnRow = functionsRow.AddRow(); fnRow.SetData(null, APIPageFunction { name, function = fn }); fnRow.icon = mainForm.icons[typeMethod]; fnRow.tag = (int)fn;
1767                }
1768             }            
1769          }
1770       }
1771    }
1772    
1773    if(mainNameSpace.defines.first || (comNameSpace && comNameSpace.defines.first))
1774    {
1775       for(nameSpace = mainNameSpace ; nameSpace; nameSpace = (nameSpace == mainNameSpace) ? comNameSpace : null)
1776       {
1777          if(nameSpace->defines.first)
1778          {
1779             BTNamedLink link;
1780             Definition def;
1781             for(link = (BTNamedLink)nameSpace->defines.first; link; link = (BTNamedLink)((BTNode)link).next)
1782             {
1783                def = link.data;
1784                //if(def.module == module)
1785                {
1786                   char * name = ( name = RSearchString(def.name, "::", strlen(def.name), false, false), name ? name + 2 : def.name);
1787                   DataRow defRow;
1788                   if(!definesRow) { definesRow = row.AddRow(); definesRow.SetData(null, APIPage { $"Definitions", page = page }); definesRow.collapsed = true; definesRow.icon = mainForm.icons[typeData]; definesRow.tag = 3; };
1789                   defRow = definesRow.AddRow(); defRow.SetData(null, APIPage { name, page = page }); defRow.icon = mainForm.icons[typeData]; defRow.tag = (int)def;
1790                }
1791             }            
1792          }
1793       }
1794    }
1795 }
1796
1797 static void AddDataMemberToPage(File f, DataMember member, int indent, bool showPrivate)
1798 {
1799    char string[1024];
1800    int c;
1801    if(!member.dataType)
1802       member.dataType = ProcessTypeString(member.dataTypeString, false);
1803
1804    f.Printf("<TR>");
1805    string[0] = 0;
1806    DocPrintType(member.dataType, string, true, false);
1807
1808    f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a>", member);
1809    for(c = 0; c<indent; c++)
1810       f.Printf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
1811    f.Printf("<img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", iconNames[typeData], member.name ? member.name : ((member.type == structMember) ? "(struct)" : "(union)"));
1812    f.Printf("<TD valign=top height=22 nowrap=1>%s</TD>", (member.type == normalMember) ? string : "");
1813    if(member.type == normalMember)
1814    {
1815       char * desc = ReadDoc(member._class.module, classDoc, member._class, memberDescription, member);
1816       if(desc)
1817       {
1818          if(editing)
1819          {
1820             char fileName[MAX_LOCATION];
1821             FigureFileName(fileName, member._class.module, classDoc, member._class, memberDescription, member);
1822             f.Printf("<TD valign=top height=22><a style=\"text-decoration:none;\" href=\"edit://%s\">", fileName);
1823             f.Puts(desc);
1824             f.Printf("</a></TD>");
1825          }
1826          else
1827             f.Printf("<TD valign=top height=22>%s</TD>", desc);
1828          delete desc;
1829       }
1830    }
1831    else
1832       f.Printf("<TD valign=top height=22></TD>");
1833    
1834    if(member.type != normalMember)
1835    {
1836       DataMember subMember;
1837       for(subMember = member.members.first; subMember; subMember = subMember.next)
1838       {
1839          if((subMember.memberAccess == publicAccess || (subMember.memberAccess == privateAccess && showPrivate)))
1840          {
1841             AddDataMemberToPage(f, subMember, indent + 1, showPrivate);
1842          }
1843       }
1844    }
1845    f.Printf("</TR><br>\n");
1846 }
1847
1848 static void AddDataMember(DataRow parentRow, APIPage page, DataMember member)
1849 {
1850    DataRow row;
1851    if(member.type == normalMember)
1852    {
1853       row = parentRow.AddRow(); row.SetData(null, APIPage { member.name, page = page }); row.icon = mainForm.icons[typeData];
1854       row.tag = (int)member;
1855    }
1856    else
1857    {
1858       DataMember m;
1859       row = parentRow.AddRow(); row.SetData(null, APIPage { (member.type == unionMember) ? "(union)" : "(struct)", page });
1860       row.icon = mainForm.icons[typeData];
1861       row.tag = (int)member;
1862
1863       for(m = member.members.first; m; m = m.next)
1864       {
1865          if(m.memberAccess == publicAccess || (m.memberAccess == privateAccess && page.showPrivate))
1866             AddDataMember(row, page, m);
1867       }
1868    }
1869 }
1870
1871 static void AddClass(DataRow parentRow, Module module, Class cl, char * nsName, bool showPrivate)
1872 {
1873    char fileName[MAX_LOCATION];
1874    char string[1024];
1875    Method method;
1876    Property prop;
1877    DataMember member;
1878    Type param;
1879    DataRow row;
1880    DataRow methodsRow = null, virtualsRow = null, eventsRow = null;
1881    DataRow propertiesRow = null, membersRow = null, conversionsRow = null, enumRow = null;
1882    APIPage page;
1883
1884    row = parentRow.AddRow();
1885    row.SetData(null, (page = APIPageClass { cl.name, cl = cl, showPrivate = showPrivate }));
1886    row.tag = (int)cl;
1887    row.collapsed = true;
1888    row.icon = (cl.type == enumClass || cl.type == unitClass || cl.type == systemClass) ? mainForm.icons[typeDataType] : mainForm.icons[typeClass];
1889
1890    // METHODS
1891    if(cl.methods.first)
1892    {
1893       for(method = (Method)cl.methods.first; method; method = (Method)((BTNode)method).next)
1894       {
1895          if(method.memberAccess == publicAccess || (method.memberAccess == privateAccess && showPrivate))
1896          {
1897             DataRow mRow;
1898             if(!method.dataType)
1899                ProcessMethodType(method);
1900             if(method.type == virtualMethod)
1901             {
1902                if(method.dataType.thisClass)
1903                {
1904                   if(!eventsRow) { eventsRow = row.AddRow(); eventsRow.SetData(null, APIPage { $"Events", page = page }); eventsRow.collapsed = true; eventsRow.icon = mainForm.icons[typeEvent];  eventsRow.tag = 4; }
1905                   mRow = eventsRow.AddRow(); mRow.SetData(null, APIPageMethod { method.name, method = method }); mRow.icon = mainForm.icons[typeEvent];
1906                   mRow.tag = (int)method;
1907                }
1908                else
1909                {
1910                   if(!virtualsRow) { virtualsRow = row.AddRow(); virtualsRow.SetData(null, APIPage { $"Virtual Methods", page = page }); virtualsRow.collapsed = true; virtualsRow.icon = mainForm.icons[typeMethod]; virtualsRow.tag = 4; }
1911                   mRow = virtualsRow.AddRow(); mRow.SetData(null, APIPageMethod { method.name, method = method }); mRow.icon = mainForm.icons[typeMethod];
1912                   mRow.tag = (int)method;
1913                }
1914             }
1915             else
1916             {
1917                if(!methodsRow) { methodsRow = row.AddRow(); methodsRow.SetData(null, APIPage { $"Methods", page = page }); methodsRow.collapsed = true; methodsRow.icon = mainForm.icons[typeMethod]; methodsRow.tag = 5; }
1918                mRow = methodsRow.AddRow(); mRow.SetData(null, APIPageMethod { method.name, method = method }); mRow.icon = mainForm.icons[typeMethod];
1919                mRow.tag = (int)method;
1920             }
1921          }
1922       }
1923    }
1924
1925    if(cl.membersAndProperties.first)
1926    {
1927       for(prop = (Property)cl.membersAndProperties.first; prop; prop = prop.next)
1928       {
1929          if(prop.memberAccess == publicAccess || (prop.memberAccess == privateAccess && showPrivate))
1930          {
1931             if(!prop.dataType)
1932                prop.dataType = ProcessTypeString(prop.dataTypeString, false);
1933             if(prop.isProperty)
1934             {
1935                DataRow mRow;
1936                if(!propertiesRow) { propertiesRow = row.AddRow(); propertiesRow.SetData(null, APIPage { $"Properties", page = page }); propertiesRow.collapsed = true; propertiesRow.icon = mainForm.icons[typeProperty]; propertiesRow.tag = 6; }
1937                mRow = propertiesRow.AddRow(); mRow.SetData(null, APIPage { prop.name, page }); mRow.icon = mainForm.icons[typeProperty];
1938                mRow.tag = (int)prop;
1939             }
1940             else
1941             {
1942                if(!membersRow) { membersRow = row.AddRow(); membersRow.SetData(null, APIPage { $"Data Members", page = page }); membersRow.collapsed = true; membersRow.icon = mainForm.icons[typeData]; membersRow.tag = 6; }
1943                AddDataMember(membersRow, page, (DataMember)prop);
1944             }
1945          }
1946       }
1947    }
1948
1949    if(cl.conversions.first)
1950    {
1951       for(prop = cl.conversions.first; prop; prop = prop.next)
1952       {
1953          DataRow mRow;
1954          char * name;
1955          if(!conversionsRow) { conversionsRow = row.AddRow(); conversionsRow.SetData(null, APIPage { $"Conversions", page = page }); conversionsRow.collapsed = true; conversionsRow.icon = mainForm.icons[typeDataType]; conversionsRow.tag = 7; }
1956          name = RSearchString(prop.name, "::", strlen(prop.name), true, false);
1957          if(name) name += 2; else name = prop.name;
1958          mRow = conversionsRow.AddRow(); mRow.SetData(null, APIPage { name, page = page }); mRow.icon = mainForm.icons[typeDataType];
1959          mRow.tag = (int)prop;
1960       }
1961    }
1962    if(cl.type == enumClass)
1963    {
1964       EnumClassData enumeration = (EnumClassData)cl.data;
1965       NamedLink item;
1966       for(item = enumeration.values.first; item; item = item.next)
1967       {
1968          DataRow mRow;                                                                                                                                                                                      
1969          if(!enumRow) { enumRow = row.AddRow(); enumRow.SetData(null, APIPage { $"Enumeration Values", page = page }); enumRow.collapsed = true; enumRow.icon = mainForm.icons[typeEnumValue]; enumRow.tag = 8; }
1970          mRow = enumRow.AddRow(); mRow.SetData(null, APIPage { item.name, page = page }); mRow.icon = mainForm.icons[typeEnumValue];         
1971          mRow.tag = (int)item;
1972       }
1973    }
1974 }
1975
1976 class MainForm : Window
1977 {
1978    size = { 1000, 600 };
1979    hasClose = true;
1980    borderStyle = sizable;
1981    hasMaximize = true;
1982    hasMinimize = true;
1983    nativeDecorations = true;
1984    icon = { ":documentorIcon.png" };
1985    text = $"API Documentation Browser";
1986
1987    BitmapResource icons[CodeObjectType];
1988
1989    MainForm()
1990    {
1991       CodeObjectType c;
1992       for(c = 0; c < CodeObjectType::enumSize; c++)
1993       {
1994          icons[c] = BitmapResource { iconNames[c], window = this, alphaBlend = true };
1995       }
1996       browser.AddField(DataField { dataType = class(APIPage) });
1997    }
1998
1999    hasMenuBar = true;
2000    menu = Menu { };
2001    Menu fileMenu { menu, $"File", f };
2002    Array<FileFilter> fileFilters
2003    { [
2004       { $"eC Shared Library files (*.dll, *.so, *.dylib)", "dll, so, dylib" },
2005       { $"eC Symbol files (*.sym)", "sym" }
2006    ] };
2007
2008    FileDialog fileDialog
2009    {
2010       filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter)
2011    };
2012    MenuItem fileOpenItem
2013    {
2014       fileMenu, $"Open...", o, ctrlO;
2015
2016       bool NotifySelect(MenuItem selection, Modifiers mods)
2017       {
2018          if(fileDialog.Modal() == ok)
2019          {
2020             OpenModule(fileDialog.filePath);
2021          }
2022          return true;
2023       }
2024    };
2025    MenuItem fileSettingsItem
2026    {
2027       fileMenu, $"Settings...", s, ctrlS; // set the Settings item to the file menu with shortcut keys:s and ctrl+s
2028
2029       bool NotifySelect(MenuItem selection, Modifiers mods)
2030       {
2031          SettingsDialog { master = this }.Modal(); // Open the settings dialog to allow the user to change the directory for the eCdoc files
2032       }
2033    };
2034    MenuDivider { fileMenu };
2035    MenuItem fileExit { fileMenu, $"Exit", x, altF4, NotifySelect = MenuFileExit };
2036
2037    void OpenModule(char * filePath)
2038    {
2039       char extension[MAX_EXTENSION];
2040       Module module = null;
2041       static char symbolsDir[MAX_LOCATION];
2042
2043       FreeContext(globalContext);
2044       FreeExcludedSymbols(excludedSymbols);
2045       ::defines.Free(FreeModuleDefine);
2046       imports.Free(FreeModuleImport);
2047       
2048       FreeGlobalData(globalData);
2049       FreeTypeData(componentsApp);
2050       FreeIncludeFiles();
2051       delete componentsApp;
2052
2053       SetGlobalContext(globalContext);
2054       componentsApp = __ecere_COM_Initialize(false, 1, null);
2055       SetPrivateModule(componentsApp);
2056
2057       StripLastDirectory(filePath, symbolsDir);
2058       SetSymbolsDir(symbolsDir);
2059
2060       GetExtension(filePath, extension);
2061
2062       mainForm.browser.Clear();
2063
2064       ImportModule(filePath, normalImport, publicAccess, false);
2065
2066       if(extension[0] && strcmpi(extension, "so") && strcmpi(extension, "dll"))
2067          componentsApp.name = CopyString(filePath);
2068       
2069       for(module = componentsApp.allModules.first; module; module = module.next)
2070       {
2071          if(module.name && (!strcmp(module.name, "ecere") || !strcmp(module.name, "ecereCOM")))
2072             break;
2073       }
2074       if(!module)
2075          eModule_LoadStrict(componentsApp, "ecereCOM", publicAccess /*privateAccess*/);
2076       AddComponents(componentsApp, false);
2077
2078       for(module = componentsApp.allModules.first; module; module = module.next)
2079       {
2080          if(module.name && (!strcmp(module.name, filePath)))
2081             break;
2082       }
2083       if(!module) module = componentsApp;
2084       mainForm.browser.SelectRow(mainForm.browser.FindSubRow((int)module));
2085
2086       SetSymbolsDir(null);
2087    }
2088
2089    ListBox browser
2090    {
2091       this, anchor = { left = 0, top = 0, bottom = 0 }, borderStyle = 0, background = aliceBlue;
2092       treeBranches = true; collapseControl = true; fullRowSelect = false; rootCollapseButton = true;
2093       hotKey = alt0;
2094
2095       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
2096       {
2097          APIPage page = row.GetData(null);
2098          if(view.edit) view.OnLeftButtonDown(0,0,0);
2099          if(page && page.page) page = page.page;
2100          view.edit = false;
2101          view.PositionCaret(true);
2102          if(page != view.page)
2103          {
2104             Window activeChild = this.activeChild;
2105
2106             view.Destroy(0);
2107             if(page)
2108                view.Create();
2109             activeChild.Activate();
2110          }
2111          else if(!view.created)
2112             view.Create();
2113          
2114          {
2115             page = row.GetData(null);
2116             if(page && page.page)
2117             {
2118                switch(row.tag)
2119                {
2120                   case 1: view.GoToAnchor("Classes"); break;
2121                   case 2: view.GoToAnchor("Functions"); break;
2122                   case 3: view.GoToAnchor("Definitions"); break;
2123                   case 4: view.GoToAnchor("VirtualMethods"); break;
2124                   case 5: view.GoToAnchor("Methods"); break;
2125                   case 6: view.GoToAnchor("Members"); break;
2126                   case 7: view.GoToAnchor("Conversions"); break;
2127                   case 8: view.GoToAnchor("EnumerationValues"); break;
2128                   default:
2129                   {
2130                      char hex[10];
2131                      sprintf(hex, "%08x", row.tag);
2132                      view.GoToAnchor(hex);
2133                   }
2134                }
2135             }
2136             else
2137             {
2138                view.SetScrollPosition(0, 0);
2139             }
2140          }
2141          return true;
2142       }
2143    };
2144    HelpView view
2145    {
2146       this, anchor = { top = 0, bottom = 0, right = 0 };
2147       hotKey = escape;
2148    };
2149    PaneSplitter slider
2150    {
2151       this, leftPane = browser, rightPane = view, split = 300 /*scaleSplit = 0.3 */
2152    };
2153
2154    bool OnClose(bool parentClosing)
2155    {
2156       if(view.edit)
2157          view.OnLeftButtonDown(0,0,0);
2158       return true;
2159    }
2160
2161    bool OnPostCreate()
2162    {
2163       mainForm.OpenModule((((GuiApplication)__thisModule).argc > 1) ? ((GuiApplication)__thisModule).argv[1] : "ecere");
2164       //mainForm.OpenModule("ec");
2165       //mainForm.OpenModule("c:/games/chess/debug/chess.sym");
2166       //mainForm.OpenModule("c:/ide/Objects.IDE.Win32.Debug/ide.sym");
2167       {
2168          int index = mainForm.browser.currentRow.index;
2169          int rowHeight = mainForm.browser.rowHeight;
2170          int height = mainForm.browser.clientSize.h;
2171
2172          mainForm.browser.scroll = { 0, index * rowHeight - height / 2 };
2173       }
2174       return true;
2175    }
2176 };
2177
2178 class EditDialog : Window
2179 {
2180    borderStyle = sizable;
2181    size = { 600, 400 };
2182    autoCreate = false;
2183
2184    EditBox editBox
2185    {
2186       this, anchor = { left = 16, top = 16, right = 18, bottom = 61 }
2187    };
2188    Button saveChanges
2189    {
2190       this, text = $"Save Changes", anchor = { horz = 184, vert = 160 }
2191    };
2192    Button cancel
2193    {
2194       this, text = $"Cancel", anchor = { horz = 254, vert = 160 }
2195    };
2196 }
2197
2198 #define UTF8_IS_FIRST(x)   (__extension__({ byte b = x; (!(b) || !((b) & 0x80) || (b) & 0x40); }))
2199 #define UTF8_NUM_BYTES(x)  (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
2200
2201 class HelpView : HTMLView
2202 {
2203    APIPage page;
2204
2205    hasVertScroll = true;
2206    hasHorzScroll = true;
2207    bool edit;
2208    char editString[MAX_LOCATION];
2209
2210    bool OnCreate()
2211    {
2212       TempFile f { };
2213       page = mainForm.browser.currentRow.GetData(null);
2214       if(page)
2215       {
2216          page.Generate(f);
2217          f.Seek(0, start);
2218          OpenFile(f, null);
2219          GoToAnchor(page.label);
2220          // Go to label...
2221          if(page.page) page = page.page;
2222       }
2223       delete f;
2224       return HTMLView::OnCreate();
2225    }
2226    EditDialog dialog
2227    {
2228
2229    };
2230
2231    void SaveEdit()
2232    {
2233       char archiveFile[MAX_LOCATION];
2234       char fileName[MAX_FILENAME];
2235       char directory[MAX_LOCATION];
2236       char * location;
2237       Archive archive;
2238       SplitArchivePath(editString, archiveFile, &location);
2239       GetLastDirectory(location, fileName);
2240       StripLastDirectory(location, directory);
2241       archive = ArchiveOpen(archiveFile, { true } );
2242       if(archive)
2243       {
2244          TempFile f { };
2245          ArchiveDir dir = archive.OpenDirectory(directory, null, replace);
2246          Block block;
2247          bool empty = true;
2248          for(block = textBlock.parent.subBlocks.first; block; block = block.next)
2249          {
2250             if(block.type == TEXT && block.textLen)
2251             {
2252                empty = false;
2253                break;
2254             }
2255          }
2256          if(!empty)
2257          {
2258             for(block = textBlock.parent.subBlocks.first; block; block = block.next)
2259             {
2260                if(block.type == BR)
2261                   f.Puts("<br>");
2262                else if(block.type == TEXT)
2263                   f.Write(block.text, 1, block.textLen);
2264             }
2265          }
2266          f.Seek(0, start);
2267          dir.AddFromFile(fileName, f, null, replace, 0, null, null);
2268          delete dir;
2269          delete archive;
2270          delete f;
2271          if(empty)
2272          {
2273             Block parent = textBlock.parent;
2274             while((block = parent.subBlocks.first))
2275             {
2276                parent.subBlocks.Remove(block);
2277                delete block;
2278             }
2279             textBlock = Block { type = TEXT, parent = parent, font = parent.font };
2280             textBlock.text = CopyString($"[Add Text]");
2281             textBlock.textLen = strlen(textBlock.text);
2282             parent.subBlocks.Add(textBlock);
2283          }
2284
2285          edit = false;            
2286          if(created)
2287          {
2288             ComputeMinSizes();
2289             ComputeSizes();
2290             PositionCaret(true);
2291             Update(null);
2292          }
2293       }
2294    }
2295
2296    bool OnLeftButtonDown(int x, int y, Modifiers mods)
2297    {
2298       if(edit)
2299       {
2300          // Update overLink
2301          HTMLView::OnMouseMove(x, y, mods);
2302          if(textBlock && overLink == textBlock.parent)
2303          {
2304             selPosition = curPosition = TextPosFromPoint(x, y, &textBlock);
2305             PositionCaret(true);            
2306          }
2307          else
2308          {
2309             SaveEdit();
2310             HTMLView::OnLeftButtonDown(x, y, mods);
2311          }
2312          return true;
2313       }
2314       return HTMLView::OnLeftButtonDown(x, y, mods);
2315    }
2316
2317    bool OnLeftButtonUp(int x, int y, Modifiers mods)
2318    {
2319       if(!edit || !textBlock || clickedLink != textBlock.parent)
2320       {
2321          HTMLView::OnLeftButtonUp(x, y, mods);
2322          if(edit)
2323          {
2324             selPosition = curPosition = TextPosFromPoint(x, y, &textBlock);
2325             PositionCaret(true);
2326          }
2327       }
2328       return true;
2329    }
2330
2331    // Returns true if it needs scrolling
2332    /*
2333    bool FindMouse(int px, int py, int * tx, int * ty, EditLine * tline, bool half)
2334    {
2335       int w;
2336       int c;
2337       int x, y;
2338       EditLine line;
2339       bool needHScroll = false;
2340
2341       if(py < 0)
2342       {
2343          if(this.viewY > 0)
2344          {
2345             y = this.viewY-1;
2346             line = this.viewLine ? (void *)this.viewLine.prev : null;
2347          }
2348          else
2349          {
2350             y = 0;
2351             line = (void *)this.lines.first;
2352          }
2353       }
2354       else
2355       {
2356          py = Min(py, clientSize.h);
2357          py /= this.space.h;
2358          py = Min(py, this.lineCount);
2359          y = this.viewY;
2360          for(c = 0, line = this.viewLine; (line != (void *)this.lines.last && c<py); line = line.next, c++)
2361          {
2362             y++;
2363          }
2364       }
2365
2366       if( (px >= clientSize.w || px < clientSize.w/2) && this.viewX)
2367          needHScroll = true;
2368       px = Max(px,0);
2369       px = Min(px,clientSize.w+this.space.w);
2370
2371       if(tx && line)
2372       {
2373          *tx = AdjustXPosition(line, px + viewX, half, null, MAXINT, 0);
2374       }
2375
2376       if(tline) *tline = line;
2377       if(ty) *ty = y;
2378
2379       // Prevent divide by 0 from non valid this.font
2380       if(!this.space.h)
2381          return (y < this.viewY) || needHScroll;
2382       else
2383          return (y < this.viewY || y >= this.viewY + clientSize.h / this.space.h) || needHScroll;
2384       return false;
2385    }
2386 */
2387 /*
2388    bool OnLeftButtonDown(int mx, int my, Modifiers mods)
2389    {
2390       int x,y;
2391       EditLine line;
2392
2393       if(style.noSelect) return true;
2394
2395       if(!mods.isActivate)
2396       {
2397          Capture();
2398          mouseSelect = true;
2399       }
2400
2401       mouseX = mx - XOFFSET;
2402       mouseY = my;
2403
2404       FindMouse(mouseX, mouseY, &x, &y, &line, true);
2405
2406       if(!style.readOnly)
2407       {
2408          if(wordSelect)
2409             mouseMove = false;
2410          else if(IsMouseOnSelection() && !mods.isActivate)
2411          {
2412             DirtyLine(this.y);
2413             mouseMove = true;
2414             dropX = x;
2415             dropY = y;
2416             dropLine = line;
2417          }
2418       }
2419
2420       if(!mouseMove && !wordSelect && (!mods.isActivate || style.multiLine))
2421       {
2422          if(mods.shift && !mods.isActivate)
2423          {
2424             this.x = x;
2425             this.y = y;
2426             this.line = line;
2427             DirtyAll();
2428          }
2429          else
2430          {
2431             SelDirty();
2432             DirtyLine(this.y);
2433             this.x = x;
2434             this.y = y;
2435             this.line = line;
2436             DirtyLine(this.y);
2437             this.selLine = this.line;
2438             this.selX = this.x;
2439             this.selY = this.y;
2440             //Deselect();
2441          }
2442          ComputeColumn();
2443       }
2444       
2445       UpdateDirty();
2446       UpdateCaretPosition(true);
2447       return true;
2448    }
2449 */
2450 /*
2451    bool OnLeftButtonUp(int x, int y, Modifiers mods)
2452    {
2453       timer.Stop();
2454
2455       mouseSelect = false;
2456       wordSelect = false;
2457       
2458       x -= XOFFSET;
2459       
2460       ReleaseCapture();
2461       if(!style.readOnly)
2462       {
2463          if(mouseMove)
2464          {
2465             EditLine line;
2466             FindMouse(mouseX, mouseY, &x, &y, &line, true);
2467     
2468             dropX = x;
2469             dropY = y;
2470             dropLine = line;
2471
2472             mouseMove = IsMouseOnSelection();
2473
2474             if(!mouseMove)
2475             {
2476                int size = SelSize();
2477                if(size)
2478                {
2479                   char * text = new char[size+1];
2480                   if(text)
2481                   {
2482                      int moveX = 0;
2483                      GetSel(text, false);
2484
2485                      if(Max(selY, this.y) == dropY)
2486                      {
2487                         if(this.x > selX)
2488                         {
2489                            if(this.dropX > this.selX)
2490                               moveX = this.x - this.selX;
2491                         }
2492                         else
2493                         {
2494                            if(this.dropX > this.x)
2495                               moveX = this.selX - this.x;
2496                         }
2497                      }
2498                      DelSel(null);
2499                      this.dropX -= moveX;
2500                      this.selX = this.x = this.dropX;
2501                      this.selY = this.y = this.dropY;
2502                      this.selLine = this.line = this.dropLine;
2503                      AddS(text);
2504                      SetViewToCursor(true);
2505                      delete text;
2506                      Modified();
2507                   }
2508                }
2509             }
2510             else
2511             {
2512                SelDirty();
2513                DirtyLine(this.y);
2514                this.x = x;
2515                this.y = y;
2516                this.line = line;
2517                ComputeColumn();
2518                DirtyLine(this.y);
2519                Deselect();
2520                UpdateDirty();
2521             }
2522          }
2523          else
2524          {
2525             EditLine line;
2526             mouseX = x;
2527             mouseY = y;
2528
2529             FindMouse(mouseX, mouseY, &x, &y, &line, true);
2530
2531             NotifyDropped(master, this, x, y);
2532          }
2533       }
2534       mouseMove = false;
2535       return true;
2536    }
2537
2538    bool OnMouseMove(int mx, int my, Modifiers mods)
2539    {
2540       int x,y;
2541       EditLine line;
2542       bool needScroll;
2543       
2544       if(mods != -1 && mods.isSideEffect) 
2545       { 
2546          SetSelectCursor(); 
2547          return true; 
2548       }
2549       if(style.noSelect) return true;
2550       if(wordSelect) return true;
2551       mouseX = mx - XOFFSET;
2552       mouseY = my;
2553
2554       needScroll = FindMouse(this.mouseX, this.mouseY, &x, &y, &line, true);
2555
2556       if(this.mouseMove || this.mouseSelect)
2557       {
2558          if(!needScroll)
2559             timer.Stop();
2560          else 
2561          {
2562             if(needScroll)
2563                timer.Start();
2564             if(mods != -1 && 
2565                ((style.hScroll) || (style.vScroll)))
2566                return true;
2567          }
2568       }
2569
2570       if(this.mouseMove)
2571       {
2572          DirtyLine(this.dropY);
2573          this.dropX = x;
2574          this.dropY = y;
2575          DirtyLine(this.dropY);
2576          this.dropLine = line;
2577          SetViewToCursor(true);
2578       }
2579       else if(this.mouseSelect)
2580       {
2581          DirtyLine(this.selY);
2582          DirtyLine(this.y);
2583          this.x = x;
2584          this.y = y;
2585          ComputeColumn();
2586          DirtyLine(this.y);
2587          this.line = line;
2588          SetViewToCursor(true);
2589          UpdateDirty();
2590       }
2591       SetSelectCursor();
2592       return true;
2593    }
2594
2595    bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
2596    {
2597       int x,y;
2598       EditLine line;
2599       
2600       mx -= XOFFSET;
2601
2602       if(style.noSelect) return true;
2603       FindMouse(mx, my, &x, &y, &line, false);
2604       if(!NotifyDoubleClick(master, this, line, mods))
2605          return false;
2606       if(x < line.count)
2607       {
2608          int c;
2609          int start = -1;
2610          int numBytes;
2611          for(c = x; c >= 0; c--)
2612          {
2613             unichar ch;
2614             while(c > 0 && !UTF8_IS_FIRST(line.buffer[c])) c--;
2615             ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
2616             if(!IS_ALUNDER(ch))
2617                break;
2618             start = c;
2619          }
2620          if(start != -1)
2621          {
2622             for(c = start; c<line.count; c += numBytes)
2623             {
2624                unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
2625                if(!IS_ALUNDER(ch))
2626                   break;
2627             }
2628             SelDirty();
2629             DirtyLine(this.y);
2630             this.y = y;
2631             DirtyLine(this.y);
2632             this.selX = start;
2633             this.x = c;
2634             ComputeColumn();
2635             this.line = this.selLine = line;
2636             this.wordSelect = (c != start);
2637             UpdateDirty();
2638          }
2639       }
2640       return true;
2641    }
2642 */
2643    bool OnOpen(char * href)
2644    {
2645       if(!strncmp(href, "api://", 6))
2646       {
2647          int tag = strtoul(href + 6, null, 16);
2648          DataRow row = mainForm.browser.FindSubRow(tag);
2649          if(row)
2650          {
2651             edit = false;
2652             mainForm.browser.SelectRow(row);
2653             while((row = row.parent))
2654                row.collapsed = false;
2655             row = mainForm.browser.currentRow;
2656             mainForm.browser.scroll = { 0, row.index * mainForm.browser.rowHeight - mainForm.browser.clientSize.h / 2 };
2657          }
2658       }
2659       else if(!strncmp(href, "edit://", 7))
2660       {
2661          Block block;
2662          int startX = clickedLink.startX, startY = clickedLink.startY;
2663          for(block = (Block)clickedLink.subBlocks.first; block; block = block.next)
2664          {
2665             if(block.type == TEXT) startX = block.startX, startY = block.startY;
2666             if(block.type == BR && (!block.prev || !block.next || block.next.type != TEXT))
2667             {
2668                Block newBlock { type = TEXT, parent = block.parent, font = block.parent.font };
2669                int tw = 0, th = 0;
2670                display.FontExtent(block.font.font, " ", 1, null, &th);
2671                if(!block.prev)
2672                {
2673                   block.parent.subBlocks.Insert(null, newBlock);
2674                   block = newBlock;
2675                }
2676                else
2677                {
2678                   block.parent.subBlocks.Insert(block, newBlock);
2679                   startY += th;
2680                }               
2681                newBlock.startX = startX;
2682                newBlock.startY = startY;
2683                newBlock.text = new0 char[1];
2684             }
2685          }
2686
2687          textBlock = (Block)clickedLink.subBlocks.first;
2688          if(!strcmp(textBlock.text, $"[Add Text]"))
2689          {
2690             textBlock.text[0] = 0;
2691             textBlock.textLen = 0;               
2692          }
2693
2694          strcpy(editString, href + 7);
2695          selPosition = curPosition = 0;
2696          // dialog.Create();
2697          edit = true;
2698          PositionCaret(true);
2699       }
2700       return true;
2701    }
2702
2703    Block textBlock;
2704    char * text;
2705    int curPosition, selPosition;
2706
2707    bool OnKeyDown(Key key, unichar ch)
2708    {
2709       if(edit)
2710       {
2711          switch(key)
2712          {
2713             case escape:
2714                OnLeftButtonDown(0,0,0);
2715                return false;
2716             case end:
2717                selPosition = curPosition = textBlock.textLen;
2718                PositionCaret(true);
2719                Update(null);
2720                break;
2721             case home:
2722                selPosition = curPosition = 0;
2723                PositionCaret(true);
2724                Update(null);
2725                break;
2726             case ctrlHome:
2727                selPosition = curPosition = 0;
2728                while(textBlock.prev)
2729                   textBlock = textBlock.prev.prev;
2730                PositionCaret(true);
2731                Update(null);
2732                return false;
2733             case ctrlEnd:
2734                while(textBlock.next && textBlock.next.next)
2735                   textBlock = textBlock.next.next;
2736                selPosition = curPosition = textBlock.textLen;
2737                PositionCaret(true);
2738                Update(null);
2739                return false;
2740          }
2741       }
2742       else
2743          return HTMLView::OnKeyDown(key, ch);
2744       return true;
2745    }
2746    bool OnKeyHit(Key key, unichar ch)
2747    {
2748       if(edit)
2749       {
2750          switch(key)
2751          {
2752             case up:
2753             {
2754                if(caretY == textBlock.startY)
2755                {
2756                   if(textBlock.prev)
2757                   {
2758                      textBlock = textBlock.prev.prev;
2759                      selPosition = curPosition = Min(curPosition, textBlock.textLen);
2760                      PositionCaret(false);
2761                      caretY = MAXINT;
2762                   }
2763                   else
2764                      return false;
2765                }
2766
2767                {
2768                   int tw = 0, th = 0;
2769                   int textPos = 0;
2770                   int sx = textBlock.startX, sy = textBlock.startY;
2771                   char * text = textBlock.text;
2772                   int maxW;
2773                   Block block = textBlock;
2774                   while(block && block.type != TD) block = block.parent;
2775                   if(block)
2776                   {
2777                      Block table = block;
2778                      while(table && table.type != TABLE) table = table.parent;
2779                      if(table)
2780                         maxW = block.w - 2* table.cellPadding;
2781                      else
2782                         maxW = clientSize.w - 10 - sx;
2783                   }
2784                   else
2785                      maxW = clientSize.w - 10 - sx;
2786                   display.FontExtent(textBlock.font.font, " ", 1, null, &th);
2787
2788                   do
2789                   {
2790                      int startPos = textPos;
2791                      int width = 0;
2792                      int x = 0;
2793                      bool lineComplete = false;
2794                      for(; textPos<textBlock.textLen && !lineComplete;)
2795                      {
2796                         int w;
2797                         int len;
2798                         char * nextSpace = strchr(text + textPos, ' ');
2799
2800                         if(nextSpace)
2801                            len = (nextSpace - (text + textPos)) + 1;
2802                         else
2803                            len = textBlock.textLen - textPos;
2804                         
2805                         display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
2806
2807                         if(x + width + w > maxW && x > 0)
2808                         {
2809                            lineComplete = true;
2810                            break;
2811                         }
2812                         textPos += len;
2813                         width += w;
2814                         if(nextSpace)
2815                         {
2816                            x += width;
2817                            width = 0;
2818                         }
2819                         if(textPos == textBlock.textLen || (sy == caretY - th && caretX <= x + width + sx))
2820                         {
2821                            x += width;
2822                            curPosition = textPos;
2823                            while(curPosition > 0 && x + sx > caretX && textPos > startPos)
2824                            {
2825                               int len;
2826                               while(curPosition > 0 && !UTF8_IS_FIRST(text[--curPosition]));
2827                               len = curPosition - startPos;
2828                               display.FontExtent(textBlock.font.font, text + startPos, len, &x, null);
2829                            }
2830                            selPosition = curPosition;
2831                            PositionCaret(false);
2832                            return false;
2833                         }
2834                      }
2835                      if(sy == caretY - th || textPos == textBlock.textLen)
2836                      {
2837                         if(textPos != textBlock.textLen)
2838                         {
2839                            int c = textPos - 1;
2840                            while(c > 0 && text[c] == ' ') c--;
2841                            selPosition = curPosition = c + 1;
2842                         }
2843                         else
2844                            selPosition = curPosition = textBlock.textLen;
2845                         PositionCaret(false);
2846                         return false;
2847                      }
2848                      sy += th;
2849                      sx = textBlock.startX;
2850                   } while(textPos < textBlock.textLen);
2851                   return false;
2852                }
2853                return false;
2854             }
2855             case down:
2856             {
2857                int tw = 0, th = 0;
2858                int textPos = 0;
2859                int sx = textBlock.startX, sy = textBlock.startY;
2860                char * text = textBlock.text;
2861                int maxW;
2862                Block block = textBlock;
2863                while(block && block.type != TD) block = block.parent;
2864                if(block)
2865                {
2866                   Block table = block;
2867                   while(table && table.type != TABLE) table = table.parent;
2868                   if(table)
2869                      maxW = block.w - 2* table.cellPadding;
2870                   else
2871                      maxW = clientSize.w - 10 - sx;
2872                }
2873                else
2874                   maxW = clientSize.w - 10 - sx;
2875                display.FontExtent(textBlock.font.font, " ", 1, null, &th);
2876
2877                while(!textPos || textPos < textBlock.textLen)
2878                {
2879                   int startPos = textPos;
2880                   int width = 0;
2881                   int x = 0;
2882                   bool lineComplete = false;
2883                   for(; (textPos < textBlock.textLen) && !lineComplete;)
2884                   {
2885                      int w;
2886                      int len;
2887                      char * nextSpace = strchr(text + textPos, ' ');
2888
2889                      if(nextSpace)
2890                         len = (nextSpace - (text + textPos)) + 1;
2891                      else
2892                         len = textBlock.textLen - textPos;
2893                      
2894                      display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
2895
2896                      if(x + width + w > maxW && x > 0)
2897                      {
2898                         lineComplete = true;
2899                         break;
2900                      }
2901                      textPos += len;
2902                      width += w;
2903                      if(nextSpace)
2904                      {
2905                         x += width;
2906                         width = 0;
2907                      }
2908                      if(sy > caretY && (textPos == textBlock.textLen || caretX <= x + width + sx))
2909                      {
2910                         curPosition = textPos;
2911                         x += width;
2912                         while(curPosition > 0 && x + sx > caretX && textPos > startPos)
2913                         {
2914                            int len;
2915                            while(curPosition > 0 && !UTF8_IS_FIRST(text[--curPosition]));
2916                            len = curPosition - startPos;
2917                            display.FontExtent(textBlock.font.font, text + startPos, len, &x, null);
2918                         }
2919                         selPosition = curPosition;
2920                         PositionCaret(false);
2921                         return false;
2922                      }
2923                   }
2924                   if(sy > caretY)
2925                   {
2926                      selPosition = curPosition = textBlock.textLen;
2927                      PositionCaret(false);
2928                      return false;
2929                   } 
2930                   else if(textPos == textBlock.textLen && textBlock.next && textBlock.next.next)
2931                   {
2932                      startPos = 0;
2933                      textPos = 0;
2934                      textBlock = textBlock.next.next;
2935                      sy = textBlock.startY;
2936                      sx = textBlock.startX;
2937                      text = textBlock.text;
2938                   }
2939                   else
2940                   {
2941                      sy += th;
2942                      sx = textBlock.startX;
2943                   }
2944                }
2945                
2946                /*if(textBlock.next && textBlock.next.next)
2947                {
2948                   textBlock = textBlock.next.next;
2949                   selPosition = curPosition = Min(curPosition, textBlock.textLen);
2950                   PositionCaret(false);
2951                }*/
2952                break;
2953             }
2954             #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
2955             case ctrlRight:
2956             {
2957                // SELECTION CTRL-RIGHT
2958                /*
2959                bool foundAlpha = false;
2960                bool found = false;
2961                Block line, lastLine;
2962                int lastC;
2963
2964                for(line = textBlock; (line && !found); line = line.next ? line.next.next : null)
2965                {
2966                   int start = (line == textBlock) ? curPosition : 0;
2967                   int c;
2968                   for(c = start; c < line.textLen; c++)
2969                   {
2970                      if(IS_ALUNDER(line.text[c]))
2971                      {
2972                         foundAlpha = true;
2973                         lastC = c;
2974                         lastLine = line;
2975                      }
2976                      else if(foundAlpha)
2977                      {
2978                         found = true;
2979                         break;
2980                      }
2981                   }
2982                   if(!found && (c != curPosition || line != textBlock))
2983                   {
2984                      found = true;
2985                      lastLine = line;
2986                      lastC = line.textLen-1;
2987                      break;
2988                   }
2989                }  
2990                if(found)
2991                {
2992                   selPosition = curPosition = lastC+1;
2993                   textBlock = lastLine;
2994                   PositionCaret(true);
2995                }
2996                */
2997                
2998                bool foundAlpha = false;
2999                bool found = false;
3000                Block line;
3001
3002                for(line = textBlock; (line && !found); line = line.next ? line.next.next : null)
3003                {
3004                   int start = (line == textBlock) ? curPosition : 0;
3005                   int c;
3006                   for(c = start; c < line.textLen; c++)
3007                   {
3008                      if(!IS_ALUNDER(line.text[c]))
3009                         foundAlpha = true;
3010                      else if(foundAlpha)
3011                      {
3012                         found = true;
3013                         selPosition = curPosition = c;
3014                         textBlock = line;
3015                         PositionCaret(true);
3016                         break;
3017                      }
3018                   }
3019                   // No next word found, 
3020                   if(!found && (c != curPosition || line != textBlock))
3021                   {
3022                      found = true;
3023                      selPosition = curPosition = line.textLen;
3024                      textBlock = line;
3025                      PositionCaret(true);
3026                   }
3027                   foundAlpha = true;
3028                }
3029                break;
3030             }
3031             case ctrlLeft:
3032             {
3033                bool foundAlpha = false;
3034                bool found = false;
3035                Block line, lastLine;
3036                int lastC;
3037
3038                for(line = textBlock; (line && !found); line = line.prev ? line.prev.prev : null)
3039                {
3040                   int start, c;
3041                   if(curPosition == 0 && line != textBlock)
3042                   {
3043                      foundAlpha = true;
3044                      lastC = line.textLen;
3045                      lastLine = line;
3046                      break;
3047                   }
3048                   if(line == textBlock) start = curPosition-1; else start = line.textLen-1;
3049                   for(c = start; c>=0; c--)
3050                   {
3051                      if(IS_ALUNDER(line.text[c]))
3052                      {
3053                         foundAlpha = true;
3054                         lastC = c;
3055                         lastLine = line;
3056                      }
3057                      else
3058                      {
3059                         if(foundAlpha)
3060                         {
3061                            found = true;
3062                            break;
3063                         }
3064                      }
3065                   }
3066                   // No next word found, 
3067                   if(!found && curPosition > 0)
3068                   {
3069                      foundAlpha = true;
3070                      lastC = 0;
3071                      lastLine = line;
3072                      break;
3073                   }
3074                }
3075                if(foundAlpha)
3076                {
3077                   textBlock = lastLine;
3078                   selPosition = curPosition = lastC;
3079                   PositionCaret(true);
3080                }
3081                break;
3082             }
3083             case right:
3084                if(curPosition < textBlock.textLen)
3085                {
3086                   curPosition += UTF8_NUM_BYTES(textBlock.text[curPosition]);
3087                   PositionCaret(true);
3088                   selPosition = curPosition;
3089                }
3090                else if(textBlock.next && textBlock.next.next)
3091                {
3092                   textBlock = textBlock.next.next;
3093                   selPosition = curPosition = 0;
3094                   PositionCaret(true);
3095                }
3096                break;
3097             case left:
3098                if(curPosition > 0)
3099                {
3100                   while(curPosition > 0 && !UTF8_IS_FIRST(textBlock.text[--curPosition]));
3101                   PositionCaret(true);
3102                   selPosition = curPosition;
3103                }
3104                else if(textBlock.prev)
3105                {
3106                   textBlock = textBlock.prev.prev;
3107                   selPosition = curPosition = textBlock.textLen;
3108                   PositionCaret(true);
3109                }
3110                break;
3111             case backSpace:
3112                if(curPosition)
3113                {
3114                   int c = curPosition;
3115                   int nb = 1;
3116                   while(c > 0 && !UTF8_IS_FIRST(textBlock.text[--c])) nb++;
3117                   memmove(textBlock.text + curPosition - nb, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3118                   textBlock.textLen -= nb;
3119                   textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
3120                   curPosition -= nb;
3121                   selPosition = curPosition;
3122                   {
3123                      //Clear(html.block);
3124                      //CreateForms(html.block);
3125                      ComputeMinSizes();
3126                      ComputeSizes();
3127                      //PositionForms();
3128                   }
3129                   PositionCaret(true);
3130                   Update(null);
3131                }
3132                else if(textBlock.prev)
3133                {
3134                   Block prev = textBlock.prev, prevBlock = textBlock.prev.prev;
3135                   prevBlock.text = renew prevBlock.text char[prevBlock.textLen + textBlock.textLen + 1];
3136                   memcpy(prevBlock.text + prevBlock.textLen, textBlock.text, textBlock.textLen + 1);
3137
3138                   selPosition = curPosition = prevBlock.textLen;
3139                   prevBlock.textLen += textBlock.textLen;
3140                   textBlock.parent.subBlocks.Remove(prev);
3141                   delete prev;
3142                   textBlock.parent.subBlocks.Remove(textBlock);                  
3143                   delete textBlock;
3144                   textBlock = prevBlock;
3145                   
3146                   {
3147                      //Clear(html.block);
3148                      //CreateForms(html.block);
3149                      ComputeMinSizes();
3150                      ComputeSizes();
3151                      //PositionForms();
3152                   }
3153                   PositionCaret(true);
3154                   Update(null);
3155                }
3156                break;
3157             case del:
3158                if(textBlock.textLen > curPosition)
3159                {
3160                   int nb = UTF8_NUM_BYTES(textBlock.text[curPosition]);
3161                   memmove(textBlock.text + curPosition, textBlock.text + curPosition + nb, textBlock.textLen - curPosition + 1 - nb + 1);
3162                   textBlock.textLen -= nb;
3163                   textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
3164                   {
3165                      //Clear(html.block);
3166                      //CreateForms(html.block);
3167                      ComputeMinSizes();
3168                      ComputeSizes();
3169                      //PositionForms();
3170                   }
3171                   PositionCaret(true);
3172                   Update(null);
3173                }
3174                else if(textBlock.next && textBlock.next.next)
3175                {
3176                   Block next = textBlock.next, nextBlock = textBlock.next.next;
3177                   textBlock.text = renew textBlock.text char[textBlock.textLen + nextBlock.textLen + 1];
3178                   memcpy(textBlock.text + textBlock.textLen, nextBlock.text, nextBlock.textLen + 1);
3179
3180                   textBlock.textLen += nextBlock.textLen;
3181                   textBlock.parent.subBlocks.Remove(next);
3182                   delete next;
3183                   textBlock.parent.subBlocks.Remove(nextBlock);                  
3184                   delete nextBlock;
3185                   
3186                   {
3187                      //Clear(html.block);
3188                      //CreateForms(html.block);
3189                      ComputeMinSizes();
3190                      ComputeSizes();
3191                      //PositionForms();
3192                   }
3193                   PositionCaret(true);
3194                   Update(null);
3195                }
3196                break;
3197             case enter:
3198             {
3199                Block block { type = BR, parent = textBlock.parent, font = textBlock.font };
3200                Block newBlock { type = TEXT, parent = textBlock.parent, font = textBlock.font };
3201                int startY = textBlock.startY, startX = textBlock.startX;
3202                int tw = 0, th = 0;
3203
3204                display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3205                textBlock.parent.subBlocks.Insert(textBlock, block);
3206                textBlock.parent.subBlocks.Insert(block, newBlock);
3207
3208                startY += th;
3209
3210                newBlock.textLen = textBlock.textLen - curPosition;
3211                newBlock.text = new char[newBlock.textLen+1];
3212                memcpy(newBlock.text, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3213                textBlock.textLen = curPosition;
3214                textBlock.text[curPosition] = 0;
3215
3216                newBlock.startY = startY;
3217                newBlock.startX = startX;
3218                selPosition = curPosition = 0;
3219                {
3220                   //Clear(html.block);
3221                   //CreateForms(html.block);
3222                   ComputeMinSizes();
3223                   ComputeSizes();
3224                   //PositionForms();
3225                }
3226                textBlock = newBlock;
3227                PositionCaret(true);
3228                Update(null);
3229                break;
3230             }
3231             case shiftInsert:
3232             case ctrlV:
3233             {
3234                ClipBoard clipBoard { };
3235                if(clipBoard.Load())
3236                {  
3237                   int c;
3238                   char * text = clipBoard.memory;
3239                   char ch;
3240                   int start = 0;
3241                   Block parent = textBlock.parent;
3242                   FontEntry font = textBlock.font;
3243                   for(c = 0; ; c++)
3244                   {
3245                      ch = text[c];
3246                      if(ch == '\n' || ch == '\r' || !ch)
3247                      {
3248                         int len = c - start;
3249                         textBlock.text = renew textBlock.text char[textBlock.textLen + 1 + len];
3250                         memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3251                         memcpy(textBlock.text + curPosition, text + start, len);
3252                         textBlock.textLen += len;
3253                         curPosition += len;
3254                         selPosition = curPosition;
3255                         if(!ch) break;
3256                         {
3257                            Block block { type = BR, parent = parent, font = font };
3258                            Block newBlock { type = TEXT, parent = parent, font = font };
3259                            int startY = textBlock.startY, startX = textBlock.startX;
3260                            int tw = 0, th = 0;
3261
3262                            display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3263                            textBlock.parent.subBlocks.Insert(textBlock, block);
3264                            textBlock.parent.subBlocks.Insert(block, newBlock);
3265
3266                            startY += th;
3267
3268                            newBlock.textLen = textBlock.textLen - curPosition;
3269                            newBlock.text = new char[newBlock.textLen+1];
3270                            memcpy(newBlock.text, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3271                            textBlock.textLen = curPosition;
3272                            textBlock.text[curPosition] = 0;
3273
3274                            newBlock.startY = startY;
3275                            newBlock.startX = startX;
3276                            selPosition = curPosition = 0;
3277                            textBlock = newBlock;
3278                         }
3279                         if(ch == '\r' && text[c+1] == '\n') c++;
3280                         start = c + 1;
3281                      }
3282                   }
3283                   ComputeMinSizes();
3284                   ComputeSizes();
3285                   PositionCaret(true);
3286                   Update(null);
3287                }
3288                delete clipBoard;
3289                break;
3290             }
3291             default:
3292             {
3293                // eC BUG HERE: (Should be fixed)
3294                if(!key.ctrl && !key.alt && ch >= 32 && ch != 128 /*&& ch < 128*/)
3295                {
3296                   char string[5];
3297                   int len = UTF32toUTF8Len(&ch, 1, string, 5);
3298                   int c;
3299
3300                   textBlock.text = renew textBlock.text char[textBlock.textLen + len + 1];
3301                   memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3302                   
3303                   for(c = 0; c<len; c++)
3304                   {
3305                      textBlock.text[curPosition] = string[c];
3306                      textBlock.textLen++;
3307                      curPosition++;
3308                   }
3309                   selPosition = curPosition;
3310                   
3311                   {
3312                      //Clear(html.block);
3313                      //CreateForms(html.block);
3314                      ComputeMinSizes();
3315                      ComputeSizes();
3316                      //PositionForms();
3317                   }
3318                   PositionCaret(true);
3319                   Update(null);
3320                }
3321             }
3322          }
3323       }
3324       return true;
3325    }
3326
3327    void OnResize(int width, int height)
3328    {
3329       HTMLView::OnResize(width, height);
3330       PositionCaret(true);
3331    }
3332
3333    int caretX, caretY;
3334    void PositionCaret(bool setCaretX)
3335    {
3336       if(edit)
3337       {
3338          int tw = 0, th = 0;
3339          int textPos = 0;
3340          int sx = textBlock.startX, sy = textBlock.startY;
3341          char * text = textBlock.text;
3342          int maxW;
3343          Block block = textBlock;
3344          while(block && block.type != TD) block = block.parent;
3345          if(block)
3346          {
3347             Block table = block;
3348             while(table && table.type != TABLE) table = table.parent;
3349             if(table)
3350                maxW = block.w - 2* table.cellPadding;
3351             else
3352                maxW = clientSize.w - 10 - sx;
3353          }
3354          else
3355             maxW = clientSize.w - 10 - sx;
3356          
3357          display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3358
3359          while(textPos < textBlock.textLen)
3360          {
3361             int startPos = textPos;
3362             int width = 0;
3363             int x = 0;
3364             bool lineComplete = false;
3365
3366             for(; textPos<textBlock.textLen && !lineComplete;)
3367             {
3368                int w;
3369                int len;
3370                char * nextSpace = strchr(text + textPos, ' ');
3371
3372                if(nextSpace)
3373                   len = (nextSpace - (text + textPos)) + 1;
3374                else
3375                   len = textBlock.textLen - textPos;
3376                
3377                display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
3378
3379                if(x + width + w > maxW && x > 0)
3380                {
3381                   lineComplete = true;
3382                   break;
3383                }
3384                textPos += len;
3385
3386                width += w;
3387
3388                if(nextSpace)
3389                {
3390                   x += width;
3391                   width = 0;
3392                }
3393             }
3394             if(curPosition < textPos || textPos == textBlock.textLen)
3395             {
3396                int len = curPosition - startPos;
3397                display.FontExtent(textBlock.font.font, text + startPos, len, &tw, null);
3398                sx += tw;
3399                break;            
3400             }
3401             sy += th;
3402             sx = textBlock.startX;
3403          }
3404          if(setCaretX)
3405             caretX = sx;
3406          caretY = sy;
3407          SetCaret(sx, sy, th);
3408          {
3409             Point scrollPos = scroll;
3410             bool doScroll = false;
3411             if(sy - scroll.y + th > clientSize.h)
3412             {
3413                scrollPos.y = sy + th - clientSize.h;
3414                doScroll = true;
3415             }
3416             else if(sy - scroll.y < 0)
3417             {
3418                scrollPos.y = sy;
3419                doScroll = true;            
3420             }
3421             if(sx - scroll.x + 10 > clientSize.w)
3422             {
3423                scrollPos.x = sx + 10 - clientSize.w;
3424                doScroll = true;
3425             }
3426             else if(sx - scroll.x < 10)
3427             {
3428                scrollPos.x = sx - 10;
3429                doScroll = true;            
3430             }
3431             if(doScroll)
3432                scroll = scrollPos;
3433          }
3434       }
3435       else
3436          SetCaret(0,0,0);
3437    }
3438
3439    // Returns a character offset into the TextBlock from a window coordinate
3440    int TextPosFromPoint(int px, int py, Block * block)
3441    {
3442       Block parentBlock = this.textBlock.parent;
3443       Block textBlock;
3444       int result = 0;
3445       *block = this.textBlock;
3446
3447       px += scroll.x;
3448       py += scroll.y;
3449
3450       for(textBlock = parentBlock.subBlocks.first; textBlock; textBlock = textBlock.next)
3451       {
3452          int sx = textBlock.startX, sy = textBlock.startY;
3453          int th = 0;
3454          int textPos = 0;
3455          char * text = textBlock.text;
3456          int maxW;
3457          Block b = textBlock;
3458          int space;
3459
3460          if(textBlock.type != TEXT) continue;
3461
3462          while(b && b.type != TD) b = b.parent;
3463          if(b)
3464          {
3465             Block table = b;
3466             while(table && table.type != TABLE) table = table.parent;
3467             if(table)
3468                maxW = b.w - 2* table.cellPadding;
3469             else
3470                maxW = clientSize.w - 10 - sx;
3471          }
3472          else
3473             maxW = clientSize.w - 10 - sx;
3474          
3475          display.FontExtent(textBlock.font.font, " ", 1, &space, &th);
3476          //space = space/2+2;
3477          space = 2;
3478
3479          while(textPos < textBlock.textLen)
3480          {
3481             int startPos = textPos;
3482             int width = 0;
3483             int x = 0;
3484             bool lineComplete = false;
3485
3486             for(; textPos<textBlock.textLen && !lineComplete;)
3487             {
3488                int w;
3489                int len;
3490                char * nextSpace = strchr(text + textPos, ' ');
3491
3492                if(nextSpace)
3493                   len = (nextSpace - (text + textPos)) + 1;
3494                else
3495                   len = textBlock.textLen - textPos;
3496                
3497                display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
3498
3499                sx = x + textBlock.startX;
3500                if(/*py >= sy && */py < sy + th && /*px >= sx-space && */px < sx + w-space)
3501                {
3502                   int c, numBytes;
3503                   char ch;
3504                   *block = textBlock;
3505                   for(c = textPos; (ch = text[c]); c += numBytes)
3506                   {
3507                      numBytes = UTF8_NUM_BYTES(ch);
3508                      display.FontExtent(textBlock.font.font, text + c, numBytes, &w, &th);
3509                      if(/*py >= sy && */py < sy + th && /*px >= sx-w/2-space && */px < sx + w -w/2-space)
3510                         break;
3511                      sx += w;
3512                   }
3513                   return c;
3514                }
3515
3516                if(x + width + w > maxW && x > 0)
3517                {
3518                   lineComplete = true;
3519                   break;
3520                }
3521                textPos += len;
3522
3523                width += w;
3524
3525                if(nextSpace)
3526                {
3527                   x += width;
3528                   width = 0;
3529                }
3530             }
3531             if(/*py >= sy && */py < sy + th)
3532             {
3533                *block = textBlock;
3534                return textBlock.textLen;
3535             }
3536             sy += th;
3537          }
3538          *block = textBlock;
3539          result = textBlock.textLen;
3540       }
3541       return result;
3542    }
3543 }
3544
3545 Application componentsApp;
3546
3547 class Documentor : GuiApplication
3548 {
3549    bool Init()
3550    {
3551       Platform os = GetRuntimePlatform();
3552       componentsApp = __ecere_COM_Initialize(false, 1, null);
3553       SetPrivateModule(componentsApp);
3554       SetGlobalContext(globalContext);
3555       SetExcludedSymbols(&excludedSymbols);
3556       SetDefines(&::defines);
3557       SetImports(&imports);
3558       
3559       SetGlobalData(globalData);
3560
3561       settingsContainer.dataOwner = &settings;
3562       settingsContainer.Load();
3563       if(!settings.docDir || !settings.docDir[0] )
3564       {
3565          if(os == win32) // if Windows OS then
3566          {
3567             char programFilesDir[MAX_LOCATION];
3568             char appData[MAX_LOCATION]; 
3569             char homeDrive[MAX_LOCATION];
3570             char winDir[MAX_LOCATION];
3571             GetEnvironment("APPDATA", appData, sizeof(appData));
3572             GetEnvironment("HOMEDRIVE", homeDrive, sizeof(homeDrive));
3573             GetEnvironment("windir", winDir, sizeof(winDir));
3574             if(GetEnvironment("ProgramFiles", programFilesDir, MAX_LOCATION))
3575             {
3576                PathCat(programFilesDir, "ECERE SDK\\doc");
3577                settings.docDir = programFilesDir;
3578             }
3579             else if(homeDrive && homeDrive[0])
3580             {
3581                PathCat(homeDrive, "ECERE SDK\\doc");
3582                settings.docDir = homeDrive;
3583             }
3584             else if(winDir && winDir[0])
3585             {
3586                PathCat(winDir, "..\\ECERE SDK\\doc");
3587                settings.docDir = winDir;
3588             }
3589             else
3590                settings.docDir = "C:\\ECERE SDK\\doc";
3591          }
3592          else // if Os is Linux, or Mac OSX or something else
3593             settings.docDir = "/usr/share/ecere/doc/";
3594          settingsContainer.Save();
3595       }
3596
3597       //if(argc > 1)
3598       {
3599       #if 0
3600          Module module = eModule_Load(componentsApp, "ecere" /*argv[1]*/, privateAccess);
3601          DataRow row;
3602          AddComponents(module, true);
3603          mainForm.browser.currentRow = row = mainForm.browser.FindSubRow((int)module);
3604          // mainForm.browser.currentRow = row = mainForm.browser.FindSubRow((int)eSystem_FindClass(componentsApp, "Window"));
3605          while((row = row.parent))
3606             row.collapsed = false;
3607       #endif
3608       }
3609       return true;
3610    }
3611
3612    void Terminate()
3613    {
3614       FreeContext(globalContext);
3615       FreeExcludedSymbols(excludedSymbols);
3616       ::defines.Free(FreeModuleDefine);
3617       imports.Free(FreeModuleImport);
3618
3619       FreeGlobalData(globalData);
3620       FreeTypeData(componentsApp);
3621       FreeIncludeFiles();
3622       delete componentsApp;
3623    }
3624 }
3625
3626 MainForm mainForm { };