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