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