documentor: Fixed crash on doc dir refresh
[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             archive = ArchiveOpen(docFile, { true } );
2288             readOnly = archive == null;
2289             delete archive;
2290          }
2291
2292          page.Generate(f);
2293          f.Seek(0, start);
2294          OpenFile(f, null);
2295          GoToAnchor(page.label);
2296          // Go to label...
2297          if(page.page) page = page.page;
2298       }
2299       delete f;
2300       return HTMLView::OnCreate();
2301    }
2302    EditDialog dialog
2303    {
2304
2305    };
2306
2307    void SaveEdit()
2308    {
2309       char archiveFile[MAX_LOCATION];
2310       char fileName[MAX_FILENAME];
2311       char directory[MAX_LOCATION];
2312       char * location;
2313       Archive archive = null;
2314       if(SplitArchivePath(editString, archiveFile, &location))
2315       {
2316          GetLastDirectory(location, fileName);
2317          StripLastDirectory(location, directory);
2318          archive = ArchiveOpen(archiveFile, { true } );
2319       }
2320       {
2321          TempFile f { };
2322          ArchiveDir dir = archive ? archive.OpenDirectory(directory, null, replace) : null;
2323          Block block;
2324          bool empty = true;
2325          for(block = textBlock.parent.subBlocks.first; block; block = block.next)
2326          {
2327             if(block.type == TEXT && block.textLen)
2328             {
2329                empty = false;
2330                break;
2331             }
2332          }
2333          if(!empty)
2334          {
2335             for(block = textBlock.parent.subBlocks.first; block; block = block.next)
2336             {
2337                if(block.type == BR)
2338                   f.Puts("<br>");
2339                else if(block.type == TEXT)
2340                   f.Write(block.text, 1, block.textLen);
2341             }
2342          }
2343          f.Seek(0, start);
2344          if(dir)
2345             dir.AddFromFile(fileName, f, null, replace, 0, null, null);
2346          delete dir;
2347          delete archive;
2348          delete f;
2349          if(empty)
2350          {
2351             Block parent = textBlock.parent;
2352             while((block = parent.subBlocks.first))
2353             {
2354                parent.subBlocks.Remove(block);
2355                delete block;
2356             }
2357             textBlock = Block { type = TEXT, parent = parent, font = parent.font };
2358             textBlock.text = CopyString($"[Add Text]");
2359             textBlock.textLen = strlen(textBlock.text);
2360             parent.subBlocks.Add(textBlock);
2361          }
2362
2363          edit = false;
2364          if(created)
2365          {
2366             ComputeMinSizes();
2367             ComputeSizes();
2368             PositionCaret(true);
2369             Update(null);
2370          }
2371       }
2372    }
2373
2374    bool OnLeftButtonDown(int x, int y, Modifiers mods)
2375    {
2376       bool result = true;
2377
2378       if(edit && (!textBlock || overLink != textBlock.parent))
2379       {
2380          if(!readOnly)
2381             SaveEdit();
2382          HTMLView::OnLeftButtonDown(x, y, mods);
2383          selPosition = curPosition = 0;
2384          selBlock = textBlock;
2385          Update(null);
2386       }
2387       else
2388          result = HTMLView::OnLeftButtonDown(x, y, mods);
2389
2390       if(!edit && clickedLink)
2391       {
2392          ReleaseCapture();
2393          if(clickedLink == overLink && clickedLink.href)
2394          {
2395             if(OnOpen(clickedLink.href))
2396                Update(null);
2397          }
2398       }
2399
2400       if(edit)
2401       {
2402          // Update overLink
2403          if(textBlock && overLink == textBlock.parent)
2404          {
2405             selPosition = curPosition = TextPosFromPoint(x, y, &textBlock, true);
2406             selBlock = textBlock;
2407             PositionCaret(true);
2408             selecting = true;
2409             Update(null);
2410          }
2411       }
2412       return result;
2413    }
2414
2415    bool OnLeftButtonUp(int x, int y, Modifiers mods)
2416    {
2417       if(!edit || !textBlock || clickedLink != textBlock.parent)
2418       {
2419          HTMLView::OnLeftButtonUp(x, y, mods);
2420          if(edit)
2421          {
2422             selPosition = curPosition = TextPosFromPoint(x, y, &textBlock, true);
2423             selBlock = textBlock;
2424             PositionCaret(true);
2425             Update(null);
2426          }
2427       }
2428       else
2429          ReleaseCapture();
2430       selecting = false;
2431       return true;
2432    }
2433    bool selecting;
2434
2435    bool OnMouseMove(int x, int y, Modifiers mods)
2436    {
2437       if(edit && selecting)
2438       {
2439          curPosition = TextPosFromPoint(x, y, &textBlock, true);
2440          PositionCaret(true);
2441          Update(null);
2442       }
2443       return HTMLView::OnMouseMove(x, y, mods);
2444    }
2445
2446    bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
2447    {
2448       if(edit && textBlock)
2449       {
2450          int c;
2451          int start = -1;
2452          int numBytes;
2453
2454          selPosition = curPosition = TextPosFromPoint(mx, my, &textBlock, false);
2455          selBlock = textBlock;
2456          for(c = curPosition; c >= 0; c--)
2457          {
2458             unichar ch;
2459             while(c > 0 && !UTF8_IS_FIRST(textBlock.text[c])) c--;
2460             ch = UTF8GetChar(textBlock.text + c, &numBytes);
2461             if(!CharMatchCategories(ch, letters|numbers|marks|connector))
2462                break;
2463             start = c;
2464          }
2465          if(start != -1)
2466          {
2467             for(c = start; c < textBlock.textLen; c += numBytes)
2468             {
2469                unichar ch = UTF8GetChar(textBlock.text + c, &numBytes);
2470                if(!CharMatchCategories(ch, letters|numbers|marks|connector))
2471                   break;
2472             }
2473             selPosition = start;
2474             curPosition = c;
2475
2476             PositionCaret(true);
2477             Update(null);
2478             return false;
2479          }
2480       }
2481       return true;
2482    }
2483
2484    bool OnOpen(char * href)
2485    {
2486       if(!strncmp(href, "api://", 6))
2487       {
2488          int tag = (uint)strtoul(href + 6, null, 16);
2489          DataRow row = mainForm.browser.FindSubRow(tag);
2490          if(row)
2491          {
2492             edit = false;
2493             mainForm.browser.SelectRow(row);
2494             while((row = row.parent))
2495                row.collapsed = false;
2496             row = mainForm.browser.currentRow;
2497             mainForm.browser.scroll = { 0, row.index * mainForm.browser.rowHeight - mainForm.browser.clientSize.h / 2 };
2498          }
2499       }
2500       else if(!strncmp(href, "edit://", 7))
2501       {
2502          Block block;
2503          int startX = clickedLink.startX, startY = clickedLink.startY;
2504          for(block = (Block)clickedLink.subBlocks.first; block; block = block.next)
2505          {
2506             if(block.type == TEXT) startX = block.startX, startY = block.startY;
2507             if(block.type == BR && (!block.prev || !block.next || block.next.type != TEXT))
2508             {
2509                Block newBlock { type = TEXT, parent = block.parent, font = block.parent.font };
2510                int tw = 0, th = 0;
2511                display.FontExtent(block.font.font, " ", 1, null, &th);
2512                if(!block.prev)
2513                {
2514                   block.parent.subBlocks.Insert(null, newBlock);
2515                   block = newBlock;
2516                }
2517                else
2518                {
2519                   block.parent.subBlocks.Insert(block, newBlock);
2520                   startY += block.prev.height;
2521                }
2522                newBlock.startX = startX;
2523                newBlock.startY = startY;
2524                newBlock.text = new0 char[1];
2525             }
2526          }
2527
2528          textBlock = (Block)clickedLink.subBlocks.first;
2529          if(!strcmp(textBlock.text, $"[Add Text]"))
2530          {
2531             textBlock.text[0] = 0;
2532             textBlock.textLen = 0;
2533          }
2534
2535          strcpy(editString, href + 7);
2536          selPosition = curPosition = 0;
2537          selBlock = textBlock;
2538          // dialog.Create();
2539          edit = true;
2540          // PositionCaret(true);
2541       }
2542       return true;
2543    }
2544
2545    char * text;
2546
2547    void DeleteSelection()
2548    {
2549       if(textBlock != selBlock || curPosition != selPosition)
2550       {
2551          if(textBlock == selBlock)
2552          {
2553             // Within same block
2554             int start = Min(curPosition, selPosition);
2555             int end = Max(curPosition, selPosition);
2556             memmove(textBlock.text + start, textBlock.text + end, textBlock.textLen - end);
2557             textBlock.textLen -= end-start;
2558             textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
2559             curPosition = start;
2560             selPosition = start;
2561          }
2562          else
2563          {
2564             int startSel, endSel;
2565             Block startSelBlock = null, endSelBlock = null, b, next;
2566
2567             NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel);
2568
2569             startSelBlock.text = renew startSelBlock.text char[startSel + endSelBlock.textLen - endSel + 1];
2570             memcpy(startSelBlock.text + startSel, endSelBlock.text + endSel, endSelBlock.textLen - endSel + 1);
2571
2572             startSelBlock.textLen = startSel + endSelBlock.textLen - endSel;
2573             for(b = startSelBlock.next; b; b = next)
2574             {
2575                bool isEnd = b == endSelBlock;
2576                next = GetNextBlock(b);
2577                b.parent.subBlocks.Remove(b);
2578                delete b;
2579                if(isEnd)
2580                   break;
2581             }
2582             textBlock = startSelBlock;
2583             selBlock = startSelBlock;
2584             curPosition = startSel;
2585             selPosition = startSel;
2586          }
2587          ComputeMinSizes();
2588          ComputeSizes();
2589          PositionCaret(true);
2590          Update(null);
2591       }
2592    }
2593
2594    String GetSelectionString()
2595    {
2596       String selection = null;
2597       if(textBlock == selBlock)
2598       {
2599          // Within same block
2600          int start = Min(curPosition, selPosition);
2601          int end = Max(curPosition, selPosition);
2602          int len = end - start;
2603          selection = new char[len + 1];
2604          memcpy(selection, textBlock.text + start, len);
2605          selection[len] = 0;
2606       }
2607       else
2608       {
2609          int startSel, endSel;
2610          Block startSelBlock = null, endSelBlock = null, b;
2611          int totalLen = 0;
2612
2613          NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel);
2614
2615          // Compute length
2616          for(b = startSelBlock; b; b = GetNextBlock(b))
2617          {
2618             int start = (b == startSelBlock) ? startSel : 0;
2619             int end = (b == endSelBlock) ? endSel : b.textLen;
2620             int len = end - start;
2621             totalLen += len;
2622             if(b == endSelBlock)
2623                break;
2624             else if(b.type == TEXT)
2625                totalLen++;
2626          }
2627
2628          selection = new char[totalLen + 1];
2629          totalLen = 0;
2630          for(b = startSelBlock; b; b = GetNextBlock(b))
2631          {
2632             int start = (b == startSelBlock) ? startSel : 0;
2633             int end = (b == endSelBlock) ? endSel : b.textLen;
2634             int len = end - start;
2635             memcpy(selection + totalLen, b.text + start, len);
2636             totalLen += len;
2637             if(b == endSelBlock)
2638                break;
2639             else if(b.type == TEXT)
2640                selection[totalLen++] = '\n';
2641          }
2642          selection[totalLen] = 0;
2643       }
2644       return selection;
2645    }
2646
2647    void CopySelection()
2648    {
2649       String s = GetSelectionString();
2650       if(s)
2651       {
2652          int len = strlen(s);
2653          ClipBoard cb { };
2654          if(cb.Allocate(len + 1))
2655          {
2656             memcpy(cb.text, s, len + 1);
2657             cb.Save();
2658          }
2659          delete cb;
2660          delete s;
2661       }
2662    }
2663
2664    bool OnKeyDown(Key key, unichar ch)
2665    {
2666       if(edit)
2667       {
2668          switch(key)
2669          {
2670             case escape:
2671                OnLeftButtonDown(0,0,0);
2672                return false;
2673             case Key { end, shift = true }:
2674             case end:
2675                curPosition = textBlock.textLen;
2676                if(!key.shift)
2677                {
2678                   selPosition = curPosition;
2679                   selBlock = textBlock;
2680                }
2681                PositionCaret(true);
2682                Update(null);
2683                break;
2684             case Key { home, shift = true }:
2685             case home:
2686                curPosition = 0;
2687                if(!key.shift)
2688                {
2689                   selPosition = curPosition;
2690                   selBlock = textBlock;
2691                }
2692                PositionCaret(true);
2693                Update(null);
2694                break;
2695             case Key { home, ctrl = true, shift = true }:
2696             case ctrlHome:
2697                curPosition = 0;
2698                while(textBlock.prev)
2699                   textBlock = textBlock.prev.prev;
2700                if(!key.shift)
2701                {
2702                   selPosition = curPosition;
2703                   selBlock = textBlock;
2704                }
2705                PositionCaret(true);
2706                Update(null);
2707                return false;
2708             case Key { end, ctrl = true, shift = true }:
2709             case ctrlEnd:
2710                while(textBlock.next && textBlock.next.next)
2711                   textBlock = textBlock.next.next;
2712                curPosition = textBlock.textLen;
2713                if(!key.shift)
2714                {
2715                   selPosition = curPosition;
2716                   selBlock = textBlock;
2717                }
2718                PositionCaret(true);
2719                Update(null);
2720                return false;
2721          }
2722       }
2723       else
2724          return HTMLView::OnKeyDown(key, ch);
2725       return true;
2726    }
2727
2728    bool OnKeyHit(Key key, unichar ch)
2729    {
2730       if(edit)
2731       {
2732          switch(key)
2733          {
2734             case Key { up, shift = true }:
2735             case up:
2736             {
2737                if(caretY == textBlock.startY)
2738                {
2739                   if(textBlock.prev)
2740                   {
2741                      textBlock = textBlock.prev.prev;
2742                      curPosition = Min(curPosition, textBlock.textLen);
2743                      if(!key.shift)
2744                      {
2745                         selPosition = curPosition;
2746                         selBlock = textBlock;
2747                      }
2748                      Update(null);
2749                      PositionCaret(false);
2750                      caretY = MAXINT;
2751                   }
2752                   else
2753                      return false;
2754                }
2755
2756                {
2757                   int tw = 0, th = 0;
2758                   int textPos = 0;
2759                   int sx = textBlock.startX, sy = textBlock.startY;
2760                   char * text = textBlock.text;
2761                   int maxW;
2762                   Block block = textBlock;
2763                   while(block && block.type != TD) block = block.parent;
2764                   if(block)
2765                   {
2766                      Block table = block;
2767                      while(table && table.type != TABLE) table = table.parent;
2768                      if(table)
2769                         maxW = block.w - 2* table.cellPadding;
2770                      else
2771                         maxW = clientSize.w - 10 - sx;
2772                   }
2773                   else
2774                      maxW = clientSize.w - 10 - sx;
2775                   display.FontExtent(textBlock.font.font, " ", 1, null, &th);
2776
2777                   do
2778                   {
2779                      int startPos = textPos;
2780                      int width = 0;
2781                      int x = 0;
2782                      bool lineComplete = false;
2783                      for(; textPos<textBlock.textLen && !lineComplete;)
2784                      {
2785                         int w;
2786                         int len;
2787                         char * nextSpace = strchr(text + textPos, ' ');
2788
2789                         if(nextSpace)
2790                            len = (nextSpace - (text + textPos)) + 1;
2791                         else
2792                            len = textBlock.textLen - textPos;
2793                         
2794                         display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
2795
2796                         if(x + width + w > maxW && x > 0)
2797                         {
2798                            lineComplete = true;
2799                            break;
2800                         }
2801                         textPos += len;
2802                         width += w;
2803                         if(nextSpace)
2804                         {
2805                            x += width;
2806                            width = 0;
2807                         }
2808                         if(textPos == textBlock.textLen || (sy == caretY - th && caretX <= x + width + sx))
2809                         {
2810                            x += width;
2811                            curPosition = textPos;
2812                            while(curPosition > 0 && x + sx > caretX && textPos > startPos)
2813                            {
2814                               int len;
2815                               while(curPosition > 0 && !UTF8_IS_FIRST(text[--curPosition]));
2816                               len = curPosition - startPos;
2817                               display.FontExtent(textBlock.font.font, text + startPos, len, &x, null);
2818                            }
2819                            if(!key.shift)
2820                            {
2821                               selPosition = curPosition;
2822                               selBlock = textBlock;
2823                            }
2824                            Update(null);
2825
2826                            PositionCaret(false);
2827                            return false;
2828                         }
2829                      }
2830                      if(sy == caretY - th || textPos == textBlock.textLen)
2831                      {
2832                         if(textPos != textBlock.textLen)
2833                         {
2834                            int c = textPos - 1;
2835                            while(c > 0 && text[c] == ' ') c--;
2836                            curPosition = c + 1;
2837                            if(!key.shift)
2838                            {
2839                               selPosition = curPosition;
2840                               selBlock = textBlock;
2841                            }
2842                            Update(null);
2843                         }
2844                         else
2845                         {
2846                            curPosition = textBlock.textLen;
2847                            if(!key.shift)
2848                            {
2849                               selPosition = curPosition;
2850                               selBlock = textBlock;
2851                            }
2852                            Update(null);
2853                         }
2854                         PositionCaret(false);
2855                         return false;
2856                      }
2857                      sy += th;
2858                      sx = textBlock.startX;
2859                   } while(textPos < textBlock.textLen);
2860                   return false;
2861                }
2862                return false;
2863             }
2864             case Key { down, shift = true }:
2865             case down:
2866             {
2867                int tw = 0, th = 0;
2868                int textPos = 0;
2869                int sx = textBlock.startX, sy = textBlock.startY;
2870                char * text = textBlock.text;
2871                int maxW;
2872                Block block = textBlock;
2873                while(block && block.type != TD) block = block.parent;
2874                if(block)
2875                {
2876                   Block table = block;
2877                   while(table && table.type != TABLE) table = table.parent;
2878                   if(table)
2879                      maxW = block.w - 2* table.cellPadding;
2880                   else
2881                      maxW = clientSize.w - 10 - sx;
2882                }
2883                else
2884                   maxW = clientSize.w - 10 - sx;
2885                display.FontExtent(textBlock.font.font, " ", 1, null, &th);
2886
2887                while(!textPos || textPos < textBlock.textLen)
2888                {
2889                   int startPos = textPos;
2890                   int width = 0;
2891                   int x = 0;
2892                   bool lineComplete = false;
2893                   for(; (textPos < textBlock.textLen) && !lineComplete;)
2894                   {
2895                      int w;
2896                      int len;
2897                      char * nextSpace = strchr(text + textPos, ' ');
2898
2899                      if(nextSpace)
2900                         len = (nextSpace - (text + textPos)) + 1;
2901                      else
2902                         len = textBlock.textLen - textPos;
2903                      
2904                      display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
2905
2906                      if(x + width + w > maxW && x > 0)
2907                      {
2908                         lineComplete = true;
2909                         break;
2910                      }
2911                      textPos += len;
2912                      width += w;
2913                      if(nextSpace)
2914                      {
2915                         x += width;
2916                         width = 0;
2917                      }
2918                      if(sy > caretY && (textPos == textBlock.textLen || caretX <= x + width + sx))
2919                      {
2920                         curPosition = textPos;
2921                         x += width;
2922                         while(curPosition > 0 && x + sx > caretX && textPos > startPos)
2923                         {
2924                            int len;
2925                            while(curPosition > 0 && !UTF8_IS_FIRST(text[--curPosition]));
2926                            len = curPosition - startPos;
2927                            display.FontExtent(textBlock.font.font, text + startPos, len, &x, null);
2928                         }
2929                         if(!key.shift)
2930                         {
2931                            selPosition = curPosition;
2932                            selBlock = textBlock;
2933                         }
2934                         Update(null);
2935                         PositionCaret(false);
2936                         return false;
2937                      }
2938                   }
2939                   if(sy > caretY)
2940                   {
2941                      curPosition = textBlock.textLen;
2942                      if(!key.shift)
2943                      {
2944                         selPosition = curPosition;
2945                         selBlock = textBlock;
2946                      }
2947                      Update(null);
2948                      PositionCaret(false);
2949                      return false;
2950                   } 
2951                   else if(textPos == textBlock.textLen && textBlock.next && textBlock.next.next)
2952                   {
2953                      startPos = 0;
2954                      textPos = 0;
2955                      textBlock = textBlock.next.next;
2956                      sy = textBlock.startY;
2957                      sx = textBlock.startX;
2958                      text = textBlock.text;
2959                   }
2960                   else
2961                   {
2962                      sy += th;
2963                      sx = textBlock.startX;
2964                   }
2965                }
2966                
2967                /*if(textBlock.next && textBlock.next.next)
2968                {
2969                   textBlock = textBlock.next.next;
2970                   selPosition = curPosition = Min(curPosition, textBlock.textLen);
2971                   selBlock = textBlock;
2972                   PositionCaret(false);
2973                }*/
2974                break;
2975             }
2976             case Key { right, shift = true, ctrl = true }:
2977             case ctrlRight:
2978             {
2979                bool foundAlpha = false;
2980                bool found = false;
2981                Block line, lastLine;
2982                int lastC;
2983
2984                for(line = textBlock; (line && !found); line = line.next ? line.next.next : null)
2985                {
2986                   int start = (line == textBlock) ? curPosition : 0;
2987                   int c;
2988                   for(c = start; c < line.textLen; c++)
2989                   {
2990                      char ch = line.text[c];
2991                      bool isAlUnder = CharMatchCategories(ch, letters|numbers|marks|connector);
2992                      if(key.shift ? isAlUnder : !isAlUnder)
2993                      {
2994                         foundAlpha = true;
2995                         lastC = c;
2996                         lastLine = line;
2997                      }
2998                      else if(foundAlpha)
2999                      {
3000                         found = true;
3001                         if(!key.shift)
3002                         {
3003                            curPosition = c;
3004                            if(!key.shift)
3005                            {
3006                               selPosition = curPosition;
3007                               selBlock = textBlock;
3008                            }
3009                            Update(null);
3010                            textBlock = line;
3011                            PositionCaret(true);
3012                         }
3013                         break;
3014                      }
3015                   }
3016                   // No next word found, 
3017                   if(!found && (c != curPosition || line != textBlock))
3018                   {
3019                      found = true;
3020                      lastLine = line;
3021                      lastC = line.textLen-1;
3022                      if(key.shift)
3023                         break;
3024                      else
3025                      {
3026                         curPosition = line.textLen;
3027                         if(!key.shift)
3028                         {
3029                            selPosition = curPosition;
3030                            selBlock = textBlock;
3031                         }
3032                         Update(null);
3033
3034                         textBlock = line;
3035                         PositionCaret(true);
3036                      }
3037                   }
3038                   if(!key.shift)
3039                      foundAlpha = true;
3040                }
3041                if(key.shift && found)
3042                {
3043                   curPosition = lastC+1;
3044                   textBlock = lastLine;
3045                   PositionCaret(true);
3046                   Update(null);
3047                }
3048                break;
3049             }
3050             case Key { left, ctrl = true, shift = true }:
3051             case ctrlLeft:
3052             {
3053                bool foundAlpha = false;
3054                bool found = false;
3055                Block line, lastLine;
3056                int lastC;
3057
3058                for(line = textBlock; (line && !found); line = line.prev ? line.prev.prev : null)
3059                {
3060                   int start, c;
3061                   if(curPosition == 0 && line != textBlock)
3062                   {
3063                      foundAlpha = true;
3064                      lastC = line.textLen;
3065                      lastLine = line;
3066                      break;
3067                   }
3068                   if(line == textBlock) start = curPosition-1; else start = line.textLen-1;
3069                   for(c = start; c>=0; c--)
3070                   {
3071                      if(CharMatchCategories(line.text[c], letters|numbers|marks|connector))
3072                      {
3073                         foundAlpha = true;
3074                         lastC = c;
3075                         lastLine = line;
3076                      }
3077                      else
3078                      {
3079                         if(foundAlpha)
3080                         {
3081                            found = true;
3082                            break;
3083                         }
3084                      }
3085                   }
3086                   // No next word found, 
3087                   if(!found && curPosition > 0)
3088                   {
3089                      foundAlpha = true;
3090                      lastC = 0;
3091                      lastLine = line;
3092                      break;
3093                   }
3094                }
3095                if(foundAlpha)
3096                {
3097                   textBlock = lastLine;
3098                   curPosition = lastC;
3099                   if(!key.shift)
3100                   {
3101                      selPosition = curPosition;
3102                      selBlock = textBlock;
3103                   }
3104                   PositionCaret(true);
3105                   Update(null);
3106                }
3107                break;
3108             }
3109             case Key { right, shift = true }:
3110             case right:
3111                if(curPosition < textBlock.textLen)
3112                {
3113                   curPosition += UTF8_NUM_BYTES(textBlock.text[curPosition]);
3114                   if(!key.shift)
3115                   {
3116                      selPosition = curPosition;
3117                      selBlock = textBlock;
3118                   }
3119                   PositionCaret(true);
3120                   Update(null);
3121                }
3122                else if(textBlock.next && textBlock.next.next)
3123                {
3124                   textBlock = textBlock.next.next;
3125                   curPosition = 0;
3126                   if(!key.shift)
3127                   {
3128                      selPosition = curPosition;
3129                      selBlock = textBlock;
3130                   }
3131                   PositionCaret(true);
3132                   Update(null);
3133                }
3134                break;
3135             case Key { left, shift = true }:
3136             case left:
3137                if(curPosition > 0)
3138                {
3139                   while(curPosition > 0 && !UTF8_IS_FIRST(textBlock.text[--curPosition]));
3140                   if(!key.shift)
3141                   {
3142                      selPosition = curPosition;
3143                      selBlock = textBlock;
3144                   }
3145                   PositionCaret(true);
3146                   Update(null);
3147                }
3148                else if(textBlock.prev)
3149                {
3150                   textBlock = textBlock.prev.prev;
3151                   curPosition = textBlock.textLen;
3152                   if(!key.shift)
3153                   {
3154                      selPosition = curPosition;
3155                      selBlock = textBlock;
3156                   }
3157                   PositionCaret(true);
3158                   Update(null);
3159                }
3160                break;
3161             case backSpace:
3162                if(readOnly) break;
3163                if(textBlock == selBlock && curPosition == selPosition)
3164                {
3165                   if(curPosition)
3166                   {
3167                      int c = curPosition;
3168                      int nb = 1;
3169                      while(c > 0 && !UTF8_IS_FIRST(textBlock.text[--c])) nb++;
3170                      memmove(textBlock.text + curPosition - nb, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3171                      textBlock.textLen -= nb;
3172                      textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
3173                      curPosition -= nb;
3174                      selPosition = curPosition;
3175                      selBlock = textBlock;
3176
3177                      ComputeMinSizes();
3178                      ComputeSizes();
3179                      PositionCaret(true);
3180                      Update(null);
3181                   }
3182                   else if(textBlock.prev)
3183                   {
3184                      Block prev = textBlock.prev, prevBlock = textBlock.prev.prev;
3185                      prevBlock.text = renew prevBlock.text char[prevBlock.textLen + textBlock.textLen + 1];
3186                      memcpy(prevBlock.text + prevBlock.textLen, textBlock.text, textBlock.textLen + 1);
3187
3188                      selPosition = curPosition = prevBlock.textLen;
3189                      selBlock = textBlock;
3190                      prevBlock.textLen += textBlock.textLen;
3191                      textBlock.parent.subBlocks.Remove(prev);
3192                      if(prev == selBlock)
3193                      {
3194                         selBlock = textBlock;
3195                         selPosition = curPosition;
3196                      }
3197                      delete prev;
3198                      textBlock.parent.subBlocks.Remove(textBlock);
3199                      if(textBlock == selBlock)
3200                      {
3201                         selBlock = prevBlock;
3202                         selPosition = curPosition;
3203                      }
3204                      delete textBlock;
3205                      textBlock = prevBlock;
3206
3207                      ComputeMinSizes();
3208                      ComputeSizes();
3209                      PositionCaret(true);
3210                      Update(null);
3211                   }
3212                }
3213                else
3214                   DeleteSelection();
3215                break;
3216             case del:
3217                if(readOnly) break;
3218                if(textBlock != selBlock || curPosition != selPosition)
3219                   DeleteSelection();
3220                else if(textBlock.textLen > curPosition)
3221                {
3222                   int nb = UTF8_NUM_BYTES(textBlock.text[curPosition]);
3223                   memmove(textBlock.text + curPosition, textBlock.text + curPosition + nb, textBlock.textLen - curPosition + 1 - nb + 1);
3224                   textBlock.textLen -= nb;
3225                   textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
3226
3227                   ComputeMinSizes();
3228                   ComputeSizes();
3229
3230                   PositionCaret(true);
3231                   Update(null);
3232                }
3233                else if(textBlock.next && textBlock.next.next)
3234                {
3235                   Block next = textBlock.next, nextBlock = textBlock.next.next;
3236                   textBlock.text = renew textBlock.text char[textBlock.textLen + nextBlock.textLen + 1];
3237                   memcpy(textBlock.text + textBlock.textLen, nextBlock.text, nextBlock.textLen + 1);
3238
3239                   textBlock.textLen += nextBlock.textLen;
3240                   textBlock.parent.subBlocks.Remove(next);
3241                   if(next == selBlock)
3242                   {
3243                      selBlock = textBlock;
3244                      selPosition = curPosition;
3245                   }
3246                   delete next;
3247                   textBlock.parent.subBlocks.Remove(nextBlock);
3248                   if(nextBlock == selBlock)
3249                   {
3250                      selBlock = textBlock;
3251                      selPosition = curPosition;
3252                   }
3253                   delete nextBlock;
3254
3255                   ComputeMinSizes();
3256                   ComputeSizes();
3257                   PositionCaret(true);
3258                   Update(null);
3259                }
3260                break;
3261             case enter:
3262             {
3263                int tw = 0, th = 0;
3264                Block block;
3265                Block newBlock;
3266                int startY, startX;
3267
3268                if(readOnly) break;
3269                DeleteSelection();
3270
3271                block = { type = BR, parent = textBlock.parent, font = textBlock.font };
3272                newBlock = { type = TEXT, parent = textBlock.parent, font = textBlock.font };
3273                startY = textBlock.startY;
3274                startX = textBlock.startX;
3275
3276                display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3277                textBlock.parent.subBlocks.Insert(textBlock, block);
3278                textBlock.parent.subBlocks.Insert(block, newBlock);
3279
3280                startY += th;
3281
3282                newBlock.textLen = textBlock.textLen - curPosition;
3283                newBlock.text = new char[newBlock.textLen+1];
3284                memcpy(newBlock.text, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3285                textBlock.textLen = curPosition;
3286                textBlock.text[curPosition] = 0;
3287
3288                newBlock.startY = startY;
3289                newBlock.startX = startX;
3290                selPosition = curPosition = 0;
3291
3292                ComputeMinSizes();
3293                ComputeSizes();
3294
3295                textBlock = newBlock;
3296                selBlock = textBlock;
3297                PositionCaret(true);
3298                Update(null);
3299                break;
3300             }
3301             case ctrlX:
3302             case Key { del, shift = true }:
3303                if(readOnly) break;
3304                // Cut
3305                CopySelection();
3306                DeleteSelection();
3307                break;
3308             case ctrlC:
3309             case ctrlInsert:
3310                // Copy
3311                CopySelection();
3312                break;
3313             case shiftInsert:
3314             case ctrlV:
3315                if(!readOnly)
3316                {
3317                   ClipBoard clipBoard { };
3318                   if(clipBoard.Load())
3319                   {
3320                      int c;
3321                      char * text = clipBoard.memory;
3322                      char ch;
3323                      int start = 0;
3324                      Block parent;
3325                      FontEntry font;
3326
3327                      DeleteSelection();
3328
3329                      parent = textBlock.parent;
3330                      font = textBlock.font;
3331
3332                      for(c = 0; ; c++)
3333                      {
3334                         ch = text[c];
3335                         if(ch == '\n' || ch == '\r' || !ch)
3336                         {
3337                            int len = c - start;
3338                            textBlock.text = renew textBlock.text char[textBlock.textLen + 1 + len];
3339                            memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3340                            memcpy(textBlock.text + curPosition, text + start, len);
3341                            textBlock.textLen += len;
3342                            curPosition += len;
3343                            selPosition = curPosition;
3344                            selBlock = textBlock;
3345                            if(!ch) break;
3346                            {
3347                               Block block { type = BR, parent = parent, font = font };
3348                               Block newBlock { type = TEXT, parent = parent, font = font };
3349                               int startY = textBlock.startY, startX = textBlock.startX;
3350                               int tw = 0, th = 0;
3351
3352                               display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3353                               textBlock.parent.subBlocks.Insert(textBlock, block);
3354                               textBlock.parent.subBlocks.Insert(block, newBlock);
3355
3356                               startY += th;
3357
3358                               newBlock.textLen = textBlock.textLen - curPosition;
3359                               newBlock.text = new char[newBlock.textLen+1];
3360                               memcpy(newBlock.text, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3361                               textBlock.textLen = curPosition;
3362                               textBlock.text[curPosition] = 0;
3363
3364                               newBlock.startY = startY;
3365                               newBlock.startX = startX;
3366                               selPosition = curPosition = 0;
3367                               selBlock = textBlock;
3368                               textBlock = newBlock;
3369                            }
3370                            if(ch == '\r' && text[c+1] == '\n') c++;
3371                            start = c + 1;
3372                         }
3373                      }
3374                      ComputeMinSizes();
3375                      ComputeSizes();
3376                      PositionCaret(true);
3377                      Update(null);
3378                   }
3379                   delete clipBoard;
3380                }
3381                break;
3382             default:
3383             {
3384                // eC BUG HERE: (Should be fixed)
3385                if(!readOnly && !key.ctrl && !key.alt && ch >= 32 && ch != 128 /*&& ch < 128*/)
3386                {
3387                   char string[5];
3388                   int len = UTF32toUTF8Len(&ch, 1, string, 5);
3389                   int c;
3390
3391                   DeleteSelection();
3392
3393                   textBlock.text = renew textBlock.text char[textBlock.textLen + len + 1];
3394                   memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3395                   
3396                   for(c = 0; c<len; c++)
3397                   {
3398                      textBlock.text[curPosition] = string[c];
3399                      textBlock.textLen++;
3400                      curPosition++;
3401                   }
3402                   selPosition = curPosition;
3403                   selBlock = textBlock;
3404                   
3405                   {
3406                      //Clear(html.block);
3407                      //CreateForms(html.block);
3408                      ComputeMinSizes();
3409                      ComputeSizes();
3410                      //PositionForms();
3411                   }
3412                   PositionCaret(true);
3413                   Update(null);
3414                }
3415             }
3416          }
3417       }
3418       return true;
3419    }
3420
3421    void OnResize(int width, int height)
3422    {
3423       HTMLView::OnResize(width, height);
3424       PositionCaret(true);
3425    }
3426
3427    int caretX, caretY;
3428    void PositionCaret(bool setCaretX)
3429    {
3430       if(edit)
3431       {
3432          int tw = 0, th = 0;
3433          int textPos = 0;
3434          int sx = textBlock.startX, sy = textBlock.startY;
3435          char * text = textBlock.text;
3436          int maxW;
3437          Block block = textBlock;
3438          while(block && block.type != TD) block = block.parent;
3439          if(block)
3440          {
3441             Block table = block;
3442             while(table && table.type != TABLE) table = table.parent;
3443             if(table)
3444                maxW = block.w - 2* table.cellPadding;
3445             else
3446                maxW = clientSize.w - 10 - sx;
3447          }
3448          else
3449             maxW = clientSize.w - 10 - sx;
3450          
3451          display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3452
3453          while(textPos < textBlock.textLen)
3454          {
3455             int startPos = textPos;
3456             int width = 0;
3457             int x = 0;
3458             bool lineComplete = false;
3459
3460             for(; textPos<textBlock.textLen && !lineComplete;)
3461             {
3462                int w;
3463                int len;
3464                char * nextSpace = strchr(text + textPos, ' ');
3465
3466                if(nextSpace)
3467                   len = (nextSpace - (text + textPos)) + 1;
3468                else
3469                   len = textBlock.textLen - textPos;
3470                
3471                display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
3472
3473                if(x + width + w > maxW && x > 0)
3474                {
3475                   lineComplete = true;
3476                   break;
3477                }
3478                textPos += len;
3479
3480                width += w;
3481
3482                if(nextSpace)
3483                {
3484                   x += width;
3485                   width = 0;
3486                }
3487             }
3488             if(curPosition < textPos || textPos == textBlock.textLen)
3489             {
3490                int len = curPosition - startPos;
3491                display.FontExtent(textBlock.font.font, text + startPos, len, &tw, null);
3492                sx += tw;
3493                break;
3494             }
3495             sy += th;
3496             sx = textBlock.startX;
3497          }
3498          if(setCaretX)
3499             caretX = sx;
3500          caretY = sy;
3501          SetCaret(sx, sy, th);
3502          {
3503             Point scrollPos = scroll;
3504             bool doScroll = false;
3505             if(sy - scroll.y + th > clientSize.h)
3506             {
3507                scrollPos.y = sy + th - clientSize.h;
3508                doScroll = true;
3509             }
3510             else if(sy - scroll.y < 0)
3511             {
3512                scrollPos.y = sy;
3513                doScroll = true;
3514             }
3515             if(sx - scroll.x + 10 > clientSize.w)
3516             {
3517                scrollPos.x = sx + 10 - clientSize.w;
3518                doScroll = true;
3519             }
3520             else if(sx - scroll.x < 10)
3521             {
3522                scrollPos.x = sx - 10;
3523                doScroll = true;
3524             }
3525             if(doScroll)
3526                scroll = scrollPos;
3527          }
3528       }
3529       else
3530          SetCaret(0,0,0);
3531    }
3532
3533    // Returns a character offset into the TextBlock from a window coordinate
3534    int TextPosFromPoint(int px, int py, Block * block, bool half)
3535    {
3536       Block parentBlock = this.textBlock.parent;
3537       Block textBlock;
3538       int result = 0;
3539       *block = this.textBlock;
3540
3541       px += scroll.x;
3542       py += scroll.y;
3543
3544       for(textBlock = parentBlock.subBlocks.first; textBlock; textBlock = textBlock.next)
3545       {
3546          int sx = textBlock.startX, sy = textBlock.startY;
3547          int th = 0;
3548          int textPos = 0;
3549          char * text = textBlock.text;
3550          int maxW;
3551          Block b = textBlock;
3552          int space;
3553
3554          if(textBlock.type != TEXT) continue;
3555
3556          while(b && b.type != TD) b = b.parent;
3557          if(b)
3558          {
3559             Block table = b;
3560             while(table && table.type != TABLE) table = table.parent;
3561             if(table)
3562                maxW = b.w - 2* table.cellPadding;
3563             else
3564                maxW = clientSize.w - 10 - sx;
3565          }
3566          else
3567             maxW = clientSize.w - 10 - sx;
3568          
3569          display.FontExtent(textBlock.font.font, " ", 1, &space, &th);
3570          //space = space/2+2;
3571          space = 2;
3572
3573          while(textPos < textBlock.textLen)
3574          {
3575             int startPos = textPos;
3576             int width = 0;
3577             int x = 0;
3578             bool lineComplete = false;
3579
3580             for(; textPos<textBlock.textLen && !lineComplete;)
3581             {
3582                int w;
3583                int len;
3584                char * nextSpace = strchr(text + textPos, ' ');
3585
3586                if(nextSpace)
3587                   len = (nextSpace - (text + textPos)) + 1;
3588                else
3589                   len = textBlock.textLen - textPos;
3590                
3591                display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
3592
3593                sx = x + textBlock.startX;
3594                if(/*py >= sy && */py < sy + th && /*px >= sx-space && */px < sx + w-space)
3595                {
3596                   int c, numBytes;
3597                   char ch;
3598                   *block = textBlock;
3599                   for(c = textPos; (ch = text[c]); c += numBytes)
3600                   {
3601                      numBytes = UTF8_NUM_BYTES(ch);
3602                      display.FontExtent(textBlock.font.font, text + c, numBytes, &w, &th);
3603                      if(/*py >= sy && */py < sy + th && /*px >= sx-w/2-space && */px < sx + (half ? w/2 : w) -space)
3604                         break;
3605                      sx += w;
3606                   }
3607                   return c;
3608                }
3609
3610                if(x + width + w > maxW && x > 0)
3611                {
3612                   lineComplete = true;
3613                   break;
3614                }
3615                textPos += len;
3616
3617                width += w;
3618
3619                if(nextSpace)
3620                {
3621                   x += width;
3622                   width = 0;
3623                }
3624             }
3625             if(/*py >= sy && */py < sy + th)
3626             {
3627                *block = textBlock;
3628                return textBlock.textLen;
3629             }
3630             sy += th;
3631          }
3632          *block = textBlock;
3633          result = textBlock.textLen;
3634       }
3635       return result;
3636    }
3637 }
3638
3639 Application componentsApp;
3640
3641 class Documentor : GuiApplication
3642 {
3643    bool Init()
3644    {
3645       Platform os = GetRuntimePlatform();
3646       componentsApp = __ecere_COM_Initialize(false, 1, null);
3647       SetPrivateModule(componentsApp);
3648       SetGlobalContext(globalContext);
3649       SetExcludedSymbols(&excludedSymbols);
3650       SetDefines(&::defines);
3651       SetImports(&imports);
3652       
3653       SetGlobalData(globalData);
3654
3655       settingsContainer.dataOwner = &settings;
3656       settingsContainer.Load();
3657       if(!settings.docDir || !settings.docDir[0] )
3658       {
3659          if(os == win32) // if Windows OS then
3660          {
3661             char programFilesDir[MAX_LOCATION];
3662             char appData[MAX_LOCATION]; 
3663             char homeDrive[MAX_LOCATION];
3664             char winDir[MAX_LOCATION];
3665             GetEnvironment("APPDATA", appData, sizeof(appData));
3666             GetEnvironment("HOMEDRIVE", homeDrive, sizeof(homeDrive));
3667             GetEnvironment("windir", winDir, sizeof(winDir));
3668             if(GetEnvironment("ProgramFiles", programFilesDir, MAX_LOCATION))
3669             {
3670                PathCat(programFilesDir, "ECERE SDK\\doc");
3671                settings.docDir = programFilesDir;
3672             }
3673             else if(homeDrive && homeDrive[0])
3674             {
3675                PathCat(homeDrive, "ECERE SDK\\doc");
3676                settings.docDir = homeDrive;
3677             }
3678             else if(winDir && winDir[0])
3679             {
3680                PathCat(winDir, "..\\ECERE SDK\\doc");
3681                settings.docDir = winDir;
3682             }
3683             else
3684                settings.docDir = "C:\\ECERE SDK\\doc";
3685          }
3686          else // if Os is Linux, or Mac OSX or something else
3687             settings.docDir = "/usr/share/ecere/doc/";
3688          settingsContainer.Save();
3689       }
3690
3691       //if(argc > 1)
3692       {
3693       #if 0
3694          Module module = eModule_Load(componentsApp, "ecere" /*argv[1]*/, privateAccess);
3695          DataRow row;
3696          AddComponents(module, true);
3697          mainForm.browser.currentRow = row = mainForm.browser.FindSubRow((int)module);
3698          // mainForm.browser.currentRow = row = mainForm.browser.FindSubRow((int)eSystem_FindClass(componentsApp, "Window"));
3699          while((row = row.parent))
3700             row.collapsed = false;
3701       #endif
3702       }
3703       return true;
3704    }
3705
3706    void Terminate()
3707    {
3708       FreeContext(globalContext);
3709       FreeExcludedSymbols(excludedSymbols);
3710       ::defines.Free(FreeModuleDefine);
3711       imports.Free(FreeModuleImport);
3712
3713       FreeGlobalData(globalData);
3714       FreeTypeData(componentsApp);
3715       FreeIncludeFiles();
3716       delete componentsApp;
3717    }
3718 }
3719
3720 MainForm mainForm { };