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