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