ecere: Makefile;net/SSLSocket: Fixes for SSL support
[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       FreeTypeData(componentsApp);
2241       FreeIncludeFiles();
2242       delete componentsApp;
2243
2244       SetGlobalContext(globalContext);
2245       componentsApp = __ecere_COM_Initialize(false, 1, null);
2246       SetPrivateModule(componentsApp);
2247
2248       StripLastDirectory(filePath, symbolsDir);
2249       SetSymbolsDir(symbolsDir);
2250
2251       GetExtension(filePath, extension);
2252
2253       mainForm.browser.Clear();
2254
2255       ImportModule(filePath, normalImport, publicAccess, false);
2256
2257       if(extension[0] && strcmpi(extension, "so") && strcmpi(extension, "dll") && strcmpi(extension, "dylib"))
2258          componentsApp.name = CopyString(filePath);
2259
2260       for(module = componentsApp.allModules.first; module; module = module.next)
2261       {
2262          if(module.name && (!strcmp(module.name, "ecere") || !strcmp(module.name, "ecereCOM")))
2263             break;
2264       }
2265       if(!module)
2266          eModule_LoadStrict(componentsApp, "ecereCOM", publicAccess /*privateAccess*/);
2267       AddComponents(componentsApp, false);
2268
2269       GetLastDirectory(filePath, moduleName);
2270       // Extension, path and lib prefix get removed in Module::name
2271       if(extension[0])
2272       {
2273          StripExtension(moduleName);
2274          if((!strcmpi(extension, "so") || !strcmpi(extension, "dylib")) && strstr(moduleName, "lib") == moduleName)
2275          {
2276             int len = strlen(moduleName) - 3;
2277             memmove(moduleName, moduleName + 3, len);
2278             moduleName[len] = 0;
2279          }
2280       }
2281
2282       for(module = componentsApp.allModules.first; module; module = module.next)
2283       {
2284          if(module.name && (!strcmp(module.name, moduleName)))
2285             break;
2286       }
2287       if(!module) module = componentsApp;
2288       homeModule = module;
2289       mainForm.browser.SelectRow(mainForm.browser.FindSubRow((int64)module));
2290
2291       SetSymbolsDir(null);
2292    }
2293
2294    AddressBar addressBar { this, borderStyle = bevel, anchor = Anchor { top = 0, left = 0, right = 0 }, size.h = 26, hotKey = altD };
2295    ListBox browser
2296    {
2297       this, anchor = { left = 0, top = 26, bottom = 0 }, borderStyle = 0, background = aliceBlue;
2298       treeBranches = true; collapseControl = true; fullRowSelect = false; rootCollapseButton = true;
2299       hotKey = alt0;
2300
2301       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
2302       {
2303          APIPage page = row.GetData(null);
2304          if(view.edit) view.OnLeftButtonDown(0,0,0);
2305          if(page && page.page) page = page.page;
2306          view.edit = false;
2307          view.PositionCaret(true);
2308          if(page != view.page)
2309          {
2310             Window activeChild = this.activeChild;
2311
2312             // Back / Forward Support
2313             if(row && !dontRecordHistory)
2314             {
2315                if(history.count > historyPos+1)
2316                   history.count = historyPos+1;
2317                historyPos = history.count-1;
2318                addressBar.back.disabled = (historyPos == 0);
2319                addressBar.forward.disabled = (historyPos >= history.count-1);
2320
2321                history.Add((Instance)(uint64)row.tag);
2322                historyPos = history.count-1;
2323
2324                addressBar.back.disabled = (historyPos == 0);
2325                addressBar.forward.disabled = (historyPos >= history.count-1);
2326             }
2327
2328             view.Destroy(0);
2329             if(page)
2330                view.Create();
2331             activeChild.Activate();
2332          }
2333          else if(!view.created)
2334             view.Create();
2335
2336          {
2337             page = row.GetData(null);
2338             if(page && page.page)
2339             {
2340                switch(row.tag)
2341                {
2342                   case 1: view.GoToAnchor("Classes"); break;
2343                   case 2: view.GoToAnchor("Functions"); break;
2344                   case 3: view.GoToAnchor("Definitions"); break;
2345                   case 4: view.GoToAnchor("VirtualMethods"); break;
2346                   case 5: view.GoToAnchor("Methods"); break;
2347                   case 6: view.GoToAnchor("Members"); break;
2348                   case 7: view.GoToAnchor("Conversions"); break;
2349                   case 8: view.GoToAnchor("EnumerationValues"); break;
2350                   default:
2351                   {
2352                      char hex[20];
2353                      sprintf(hex, "%p", (void *)(uintptr)row.tag);
2354                      view.GoToAnchor(hex);
2355                   }
2356                }
2357             }
2358             else
2359             {
2360                view.SetScrollPosition(0, 0);
2361             }
2362          }
2363          return true;
2364       }
2365    };
2366    HelpView view
2367    {
2368       this, anchor = { top = 26, bottom = 0, right = 0 };
2369       hotKey = escape;
2370    };
2371    PaneSplitter slider
2372    {
2373       this, anchor.top = 26, leftPane = browser, rightPane = view, split = 300 /*scaleSplit = 0.3 */
2374    };
2375
2376    bool OnClose(bool parentClosing)
2377    {
2378       if(view.edit)
2379          view.OnLeftButtonDown(0,0,0);
2380       return true;
2381    }
2382
2383    bool OnPostCreate()
2384    {
2385       mainForm.OpenModule((((GuiApplication)__thisModule).argc > 1) ? ((GuiApplication)__thisModule).argv[1] : "ecere");
2386       //mainForm.OpenModule("ec");
2387       //mainForm.OpenModule("c:/games/chess/debug/chess.sym");
2388       //mainForm.OpenModule("c:/ide/Objects.IDE.Win32.Debug/ide.sym");
2389       {
2390          int index = mainForm.browser.currentRow.index;
2391          int rowHeight = mainForm.browser.rowHeight;
2392          int height = mainForm.browser.clientSize.h;
2393
2394          mainForm.browser.scroll = { 0, index * rowHeight - height / 2 };
2395       }
2396       return true;
2397    }
2398
2399    Array<Instance> history { };
2400    int historyPos;
2401    bool dontRecordHistory;
2402    Module homeModule;
2403
2404    bool Forward()
2405    {
2406       if(historyPos < history.count-1)
2407       {
2408          char location[64];
2409          historyPos++;
2410          addressBar.back.disabled = (historyPos == 0);
2411          addressBar.forward.disabled = (historyPos >= history.count-1);
2412          sprintf(location, "api://%p", history[historyPos]);
2413          dontRecordHistory = true;
2414          view.OnOpen(location);
2415          dontRecordHistory = false;
2416          return true;
2417       }
2418       return false;
2419    }
2420
2421    bool Back()
2422    {
2423       if(historyPos > 0)
2424       {
2425          char location[64];
2426          historyPos--;
2427          addressBar.back.disabled = (historyPos == 0);
2428          addressBar.forward.disabled = (historyPos >= history.count-1);
2429          sprintf(location, "api://%p", history[historyPos]);
2430          dontRecordHistory = true;
2431          view.OnOpen(location);
2432          dontRecordHistory = false;
2433          return true;
2434       }
2435       return false;
2436    }
2437
2438    void Home()
2439    {
2440       mainForm.browser.SelectRow(mainForm.browser.FindSubRow((int64)homeModule));
2441    }
2442 };
2443
2444 class EditDialog : Window
2445 {
2446    borderStyle = sizable;
2447    size = { 600, 400 };
2448    autoCreate = false;
2449
2450    EditBox editBox
2451    {
2452       this, anchor = { left = 16, top = 16, right = 18, bottom = 61 }
2453    };
2454    Button saveChanges
2455    {
2456       this, text = $"Save Changes", anchor = { horz = 184, vert = 160 }
2457    };
2458    Button cancel
2459    {
2460       this, text = $"Cancel", anchor = { horz = 254, vert = 160 }
2461    };
2462 }
2463
2464 #define UTF8_IS_FIRST(x)   (__extension__({ byte b = x; (!(b) || !((b) & 0x80) || (b) & 0x40); }))
2465 #define UTF8_NUM_BYTES(x)  (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
2466
2467 class HelpView : HTMLView
2468 {
2469    APIPage page;
2470
2471    hasVertScroll = true;
2472    hasHorzScroll = true;
2473    bool edit;
2474    char editString[MAX_LOCATION];
2475
2476    bool OnCreate()
2477    {
2478       TempFile f { };
2479
2480       page = mainForm.browser.currentRow.GetData(null);
2481       if(page)
2482       {
2483          // Writability test
2484          {
2485             char docFile[MAX_LOCATION];
2486             Archive archive;
2487             Module module = page ? page.GetModule() : null;
2488             NameSpace * ns = page ? page.GetNameSpace() : null;
2489
2490             sprintf(docFile, "%s/%s.eCdoc", settings.docDir, (!module || !module.name || (ns && ns->name && !strcmp(ns->name, "namespaces/ecere/namespaces/com"))) ? "ecereCOM" : module.name);
2491             if(FileExists(docFile))
2492             {
2493                archive = ArchiveOpen(docFile, { true } );
2494                readOnly = archive == null;
2495                delete archive;
2496             }
2497             else
2498             {
2499                readOnly = true;
2500                archive = ArchiveOpen(docFile, { true } );
2501                if(archive)
2502                {
2503                   // Must create root directory on archive creation
2504                   ArchiveDir dir = archive.OpenDirectory("", null, replace);
2505                   if(dir)
2506                      readOnly = false;
2507                   delete dir;
2508                }
2509                delete archive;
2510             }
2511          }
2512
2513          page.Generate(f);
2514          f.Seek(0, start);
2515          OpenFile(f, null);
2516          GoToAnchor(page.label);
2517          // Go to label...
2518          if(page.page) page = page.page;
2519       }
2520       delete f;
2521       return HTMLView::OnCreate();
2522    }
2523    EditDialog dialog
2524    {
2525
2526    };
2527
2528    void SaveEdit()
2529    {
2530       char archiveFile[MAX_LOCATION];
2531       char fileName[MAX_FILENAME];
2532       char directory[MAX_LOCATION];
2533       const char * location;
2534       Archive archive = null;
2535       if(SplitArchivePath(editString, archiveFile, &location))
2536       {
2537          GetLastDirectory(location, fileName);
2538          StripLastDirectory(location, directory);
2539          archive = ArchiveOpen(archiveFile, { true } );
2540       }
2541       {
2542          TempFile f { };
2543          ArchiveDir dir = archive ? archive.OpenDirectory(directory, null, replace) : null;
2544          Block block;
2545          bool empty = true;
2546          for(block = textBlock.parent.subBlocks.first; block; block = block.next)
2547          {
2548             if(block.type == TEXT && block.textLen)
2549             {
2550                empty = false;
2551                break;
2552             }
2553          }
2554          if(!empty)
2555          {
2556             for(block = textBlock.parent.subBlocks.first; block; block = block.next)
2557             {
2558                if(block.type == BR)
2559                   f.Puts("<br>");
2560                else if(block.type == TEXT)
2561                   f.Write(block.text, 1, block.textLen);
2562             }
2563          }
2564          f.Seek(0, start);
2565          if(dir)
2566             dir.AddFromFile(fileName, f, null, replace, 0, null, null);
2567          delete dir;
2568          delete archive;
2569          delete f;
2570          if(empty)
2571          {
2572             Block parent = textBlock.parent;
2573             while((block = parent.subBlocks.first))
2574             {
2575                parent.subBlocks.Remove(block);
2576                delete block;
2577             }
2578             textBlock = Block { type = TEXT, parent = parent, font = parent.font };
2579             textBlock.text = CopyString($"[Add Text]");
2580             textBlock.textLen = strlen(textBlock.text);
2581             parent.subBlocks.Add(textBlock);
2582          }
2583
2584          edit = false;
2585          if(created)
2586          {
2587             ComputeMinSizes();
2588             ComputeSizes();
2589             PositionCaret(true);
2590             Update(null);
2591          }
2592       }
2593    }
2594
2595    bool OnLeftButtonDown(int x, int y, Modifiers mods)
2596    {
2597       bool result = true;
2598
2599       if(edit && (!textBlock || overLink != textBlock.parent))
2600       {
2601          if(!readOnly)
2602             SaveEdit();
2603          HTMLView::OnLeftButtonDown(x, y, mods);
2604          selPosition = curPosition = 0;
2605          selBlock = textBlock;
2606          Update(null);
2607       }
2608       else
2609          result = HTMLView::OnLeftButtonDown(x, y, mods);
2610
2611       if(!edit && clickedLink)
2612       {
2613          ReleaseCapture();
2614          if(clickedLink == overLink && clickedLink.href)
2615          {
2616             if(OnOpen(clickedLink.href))
2617                Update(null);
2618          }
2619       }
2620
2621       if(edit)
2622       {
2623          // Update overLink
2624          if(textBlock && overLink == textBlock.parent)
2625          {
2626             selPosition = curPosition = TextPosFromPoint(x, y, &textBlock, true);
2627             selBlock = textBlock;
2628             PositionCaret(true);
2629             selecting = true;
2630             Update(null);
2631          }
2632       }
2633       return result;
2634    }
2635
2636    bool OnLeftButtonUp(int x, int y, Modifiers mods)
2637    {
2638       if(!edit || !textBlock || clickedLink != textBlock.parent)
2639       {
2640          HTMLView::OnLeftButtonUp(x, y, mods);
2641          if(edit)
2642          {
2643             selPosition = curPosition = TextPosFromPoint(x, y, &textBlock, true);
2644             selBlock = textBlock;
2645             PositionCaret(true);
2646             Update(null);
2647          }
2648       }
2649       else
2650          ReleaseCapture();
2651       selecting = false;
2652       return true;
2653    }
2654    bool selecting;
2655
2656    bool OnMouseMove(int x, int y, Modifiers mods)
2657    {
2658       if(edit && selecting)
2659       {
2660          curPosition = TextPosFromPoint(x, y, &textBlock, true);
2661          PositionCaret(true);
2662          Update(null);
2663       }
2664       return HTMLView::OnMouseMove(x, y, mods);
2665    }
2666
2667    bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
2668    {
2669       if(edit && textBlock)
2670       {
2671          int c;
2672          int start = -1;
2673          int numBytes;
2674
2675          selPosition = curPosition = TextPosFromPoint(mx, my, &textBlock, false);
2676          selBlock = textBlock;
2677          for(c = curPosition; c >= 0; c--)
2678          {
2679             unichar ch;
2680             while(c > 0 && !UTF8_IS_FIRST(textBlock.text[c])) c--;
2681             ch = UTF8GetChar(textBlock.text + c, &numBytes);
2682             if(!CharMatchCategories(ch, letters|numbers|marks|connector))
2683                break;
2684             start = c;
2685          }
2686          if(start != -1)
2687          {
2688             for(c = start; c < textBlock.textLen; c += numBytes)
2689             {
2690                unichar ch = UTF8GetChar(textBlock.text + c, &numBytes);
2691                if(!CharMatchCategories(ch, letters|numbers|marks|connector))
2692                   break;
2693             }
2694             selPosition = start;
2695             curPosition = c;
2696
2697             PositionCaret(true);
2698             Update(null);
2699             return false;
2700          }
2701       }
2702       return true;
2703    }
2704
2705    bool OnOpen(char * href)
2706    {
2707       if(!strncmp(href, "api://", 6))
2708       {
2709          int64 tag = (int64)strtoull(href + 6, null, 16);
2710          DataRow row = mainForm.browser.FindSubRow(tag);
2711          if(row)
2712          {
2713             edit = false;
2714             mainForm.browser.SelectRow(row);
2715             while((row = row.parent))
2716                row.collapsed = false;
2717             row = mainForm.browser.currentRow;
2718             mainForm.browser.scroll = { 0, row.index * mainForm.browser.rowHeight - mainForm.browser.clientSize.h / 2 };
2719          }
2720       }
2721       else if(!strncmp(href, "edit://", 7))
2722       {
2723          Block block;
2724          int startX = clickedLink.startX, startY = clickedLink.startY;
2725          for(block = (Block)clickedLink.subBlocks.first; block; block = block.next)
2726          {
2727             if(block.type == TEXT) startX = block.startX, startY = block.startY;
2728             if(block.type == BR && (!block.prev || !block.next || block.next.type != TEXT))
2729             {
2730                Block newBlock { type = TEXT, parent = block.parent, font = block.parent.font };
2731                int th = 0;
2732                display.FontExtent(block.font.font, " ", 1, null, &th);
2733                if(!block.prev)
2734                {
2735                   block.parent.subBlocks.Insert(null, newBlock);
2736                   block = newBlock;
2737                }
2738                else
2739                {
2740                   block.parent.subBlocks.Insert(block, newBlock);
2741                   startY += block.prev.height;
2742                }
2743                newBlock.startX = startX;
2744                newBlock.startY = startY;
2745                newBlock.text = new0 char[1];
2746             }
2747          }
2748
2749          textBlock = (Block)clickedLink.subBlocks.first;
2750          if(!strcmp(textBlock.text, $"[Add Text]"))
2751          {
2752             textBlock.text[0] = 0;
2753             textBlock.textLen = 0;
2754          }
2755
2756          strcpy(editString, href + 7);
2757          selPosition = curPosition = 0;
2758          selBlock = textBlock;
2759          // dialog.Create();
2760          edit = true;
2761          // PositionCaret(true);
2762       }
2763       return true;
2764    }
2765
2766    char * text;
2767
2768    void DeleteSelection()
2769    {
2770       if(textBlock != selBlock || curPosition != selPosition)
2771       {
2772          if(textBlock == selBlock)
2773          {
2774             // Within same block
2775             int start = Min(curPosition, selPosition);
2776             int end = Max(curPosition, selPosition);
2777             memmove(textBlock.text + start, textBlock.text + end, textBlock.textLen - end);
2778             textBlock.textLen -= end-start;
2779             textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
2780             curPosition = start;
2781             selPosition = start;
2782          }
2783          else
2784          {
2785             int startSel, endSel;
2786             Block startSelBlock = null, endSelBlock = null, b, next;
2787
2788             NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel);
2789
2790             startSelBlock.text = renew startSelBlock.text char[startSel + endSelBlock.textLen - endSel + 1];
2791             memcpy(startSelBlock.text + startSel, endSelBlock.text + endSel, endSelBlock.textLen - endSel + 1);
2792
2793             startSelBlock.textLen = startSel + endSelBlock.textLen - endSel;
2794             for(b = startSelBlock.next; b; b = next)
2795             {
2796                bool isEnd = b == endSelBlock;
2797                next = GetNextBlock(b);
2798                b.parent.subBlocks.Remove(b);
2799                delete b;
2800                if(isEnd)
2801                   break;
2802             }
2803             textBlock = startSelBlock;
2804             selBlock = startSelBlock;
2805             curPosition = startSel;
2806             selPosition = startSel;
2807          }
2808          ComputeMinSizes();
2809          ComputeSizes();
2810          PositionCaret(true);
2811          Update(null);
2812       }
2813    }
2814
2815    String GetSelectionString()
2816    {
2817       String selection = null;
2818       if(textBlock == selBlock)
2819       {
2820          // Within same block
2821          int start = Min(curPosition, selPosition);
2822          int end = Max(curPosition, selPosition);
2823          int len = end - start;
2824          selection = new char[len + 1];
2825          memcpy(selection, textBlock.text + start, len);
2826          selection[len] = 0;
2827       }
2828       else
2829       {
2830          int startSel, endSel;
2831          Block startSelBlock = null, endSelBlock = null, b;
2832          int totalLen = 0;
2833
2834          NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel);
2835
2836          // Compute length
2837          for(b = startSelBlock; b; b = GetNextBlock(b))
2838          {
2839             int start = (b == startSelBlock) ? startSel : 0;
2840             int end = (b == endSelBlock) ? endSel : b.textLen;
2841             int len = end - start;
2842             totalLen += len;
2843             if(b == endSelBlock)
2844                break;
2845             else if(b.type == TEXT)
2846                totalLen++;
2847          }
2848
2849          selection = new char[totalLen + 1];
2850          totalLen = 0;
2851          for(b = startSelBlock; b; b = GetNextBlock(b))
2852          {
2853             int start = (b == startSelBlock) ? startSel : 0;
2854             int end = (b == endSelBlock) ? endSel : b.textLen;
2855             int len = end - start;
2856             memcpy(selection + totalLen, b.text + start, len);
2857             totalLen += len;
2858             if(b == endSelBlock)
2859                break;
2860             else if(b.type == TEXT)
2861                selection[totalLen++] = '\n';
2862          }
2863          selection[totalLen] = 0;
2864       }
2865       return selection;
2866    }
2867
2868    void CopySelection()
2869    {
2870       String s = GetSelectionString();
2871       if(s)
2872       {
2873          int len = strlen(s);
2874          ClipBoard cb { };
2875          if(cb.Allocate(len + 1))
2876          {
2877             memcpy(cb.text, s, len + 1);
2878             cb.Save();
2879          }
2880          delete cb;
2881          delete s;
2882       }
2883    }
2884
2885    bool OnKeyDown(Key key, unichar ch)
2886    {
2887       if(edit)
2888       {
2889          switch(key)
2890          {
2891             case escape:
2892                OnLeftButtonDown(0,0,0);
2893                return false;
2894             case Key { end, shift = true }:
2895             case end:
2896                curPosition = textBlock.textLen;
2897                if(!key.shift)
2898                {
2899                   selPosition = curPosition;
2900                   selBlock = textBlock;
2901                }
2902                PositionCaret(true);
2903                Update(null);
2904                break;
2905             case Key { home, shift = true }:
2906             case home:
2907                curPosition = 0;
2908                if(!key.shift)
2909                {
2910                   selPosition = curPosition;
2911                   selBlock = textBlock;
2912                }
2913                PositionCaret(true);
2914                Update(null);
2915                break;
2916             case Key { home, ctrl = true, shift = true }:
2917             case ctrlHome:
2918                curPosition = 0;
2919                while(textBlock.prev)
2920                   textBlock = textBlock.prev.prev;
2921                if(!key.shift)
2922                {
2923                   selPosition = curPosition;
2924                   selBlock = textBlock;
2925                }
2926                PositionCaret(true);
2927                Update(null);
2928                return false;
2929             case Key { end, ctrl = true, shift = true }:
2930             case ctrlEnd:
2931                while(textBlock.next && textBlock.next.next)
2932                   textBlock = textBlock.next.next;
2933                curPosition = textBlock.textLen;
2934                if(!key.shift)
2935                {
2936                   selPosition = curPosition;
2937                   selBlock = textBlock;
2938                }
2939                PositionCaret(true);
2940                Update(null);
2941                return false;
2942          }
2943       }
2944       else
2945          return HTMLView::OnKeyDown(key, ch);
2946       return true;
2947    }
2948
2949    bool OnKeyHit(Key key, unichar ch)
2950    {
2951       if(edit)
2952       {
2953          switch(key)
2954          {
2955             case Key { up, shift = true }:
2956             case up:
2957             {
2958                if(caretY == textBlock.startY)
2959                {
2960                   if(textBlock.prev)
2961                   {
2962                      textBlock = textBlock.prev.prev;
2963                      curPosition = Min(curPosition, textBlock.textLen);
2964                      if(!key.shift)
2965                      {
2966                         selPosition = curPosition;
2967                         selBlock = textBlock;
2968                      }
2969                      Update(null);
2970                      PositionCaret(false);
2971                      caretY = MAXINT;
2972                   }
2973                   else
2974                      return false;
2975                }
2976
2977                {
2978                   int th = 0;
2979                   int textPos = 0;
2980                   int sx = textBlock.startX, sy = textBlock.startY;
2981                   char * text = textBlock.text;
2982                   int maxW;
2983                   Block block = textBlock;
2984                   while(block && block.type != TD) block = block.parent;
2985                   if(block)
2986                   {
2987                      Block table = block;
2988                      while(table && table.type != TABLE) table = table.parent;
2989                      if(table)
2990                         maxW = block.w - 2* table.cellPadding;
2991                      else
2992                         maxW = clientSize.w - 10 - sx;
2993                   }
2994                   else
2995                      maxW = clientSize.w - 10 - sx;
2996                   display.FontExtent(textBlock.font.font, " ", 1, null, &th);
2997
2998                   do
2999                   {
3000                      int startPos = textPos;
3001                      int width = 0;
3002                      int x = 0;
3003                      bool lineComplete = false;
3004                      for(; textPos<textBlock.textLen && !lineComplete;)
3005                      {
3006                         int w;
3007                         int len;
3008                         char * nextSpace = strchr(text + textPos, ' ');
3009
3010                         if(nextSpace)
3011                            len = (nextSpace - (text + textPos)) + 1;
3012                         else
3013                            len = textBlock.textLen - textPos;
3014
3015                         display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
3016
3017                         if(x + width + w > maxW && x > 0)
3018                         {
3019                            lineComplete = true;
3020                            break;
3021                         }
3022                         textPos += len;
3023                         width += w;
3024                         if(nextSpace)
3025                         {
3026                            x += width;
3027                            width = 0;
3028                         }
3029                         if(textPos == textBlock.textLen || (sy == caretY - th && caretX <= x + width + sx))
3030                         {
3031                            x += width;
3032                            curPosition = textPos;
3033                            while(curPosition > 0 && x + sx > caretX && textPos > startPos)
3034                            {
3035                               int len;
3036                               while(curPosition > 0 && !UTF8_IS_FIRST(text[--curPosition]));
3037                               len = curPosition - startPos;
3038                               display.FontExtent(textBlock.font.font, text + startPos, len, &x, null);
3039                            }
3040                            if(!key.shift)
3041                            {
3042                               selPosition = curPosition;
3043                               selBlock = textBlock;
3044                            }
3045                            Update(null);
3046
3047                            PositionCaret(false);
3048                            return false;
3049                         }
3050                      }
3051                      if(sy == caretY - th || textPos == textBlock.textLen)
3052                      {
3053                         if(textPos != textBlock.textLen)
3054                         {
3055                            int c = textPos - 1;
3056                            while(c > 0 && text[c] == ' ') c--;
3057                            curPosition = c + 1;
3058                            if(!key.shift)
3059                            {
3060                               selPosition = curPosition;
3061                               selBlock = textBlock;
3062                            }
3063                            Update(null);
3064                         }
3065                         else
3066                         {
3067                            curPosition = textBlock.textLen;
3068                            if(!key.shift)
3069                            {
3070                               selPosition = curPosition;
3071                               selBlock = textBlock;
3072                            }
3073                            Update(null);
3074                         }
3075                         PositionCaret(false);
3076                         return false;
3077                      }
3078                      sy += th;
3079                      sx = textBlock.startX;
3080                   } while(textPos < textBlock.textLen);
3081                   return false;
3082                }
3083                return false;
3084             }
3085             case Key { down, shift = true }:
3086             case down:
3087             {
3088                int th = 0;
3089                int textPos = 0;
3090                int sx = textBlock.startX, sy = textBlock.startY;
3091                char * text = textBlock.text;
3092                int maxW;
3093                Block block = textBlock;
3094                while(block && block.type != TD) block = block.parent;
3095                if(block)
3096                {
3097                   Block table = block;
3098                   while(table && table.type != TABLE) table = table.parent;
3099                   if(table)
3100                      maxW = block.w - 2* table.cellPadding;
3101                   else
3102                      maxW = clientSize.w - 10 - sx;
3103                }
3104                else
3105                   maxW = clientSize.w - 10 - sx;
3106                display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3107
3108                while(!textPos || textPos < textBlock.textLen)
3109                {
3110                   int startPos = textPos;
3111                   int width = 0;
3112                   int x = 0;
3113                   bool lineComplete = false;
3114                   for(; (textPos < textBlock.textLen) && !lineComplete;)
3115                   {
3116                      int w;
3117                      int len;
3118                      char * nextSpace = strchr(text + textPos, ' ');
3119
3120                      if(nextSpace)
3121                         len = (nextSpace - (text + textPos)) + 1;
3122                      else
3123                         len = textBlock.textLen - textPos;
3124
3125                      display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
3126
3127                      if(x + width + w > maxW && x > 0)
3128                      {
3129                         lineComplete = true;
3130                         break;
3131                      }
3132                      textPos += len;
3133                      width += w;
3134                      if(nextSpace)
3135                      {
3136                         x += width;
3137                         width = 0;
3138                      }
3139                      if(sy > caretY && (textPos == textBlock.textLen || caretX <= x + width + sx))
3140                      {
3141                         curPosition = textPos;
3142                         x += width;
3143                         while(curPosition > 0 && x + sx > caretX && textPos > startPos)
3144                         {
3145                            int len;
3146                            while(curPosition > 0 && !UTF8_IS_FIRST(text[--curPosition]));
3147                            len = curPosition - startPos;
3148                            display.FontExtent(textBlock.font.font, text + startPos, len, &x, null);
3149                         }
3150                         if(!key.shift)
3151                         {
3152                            selPosition = curPosition;
3153                            selBlock = textBlock;
3154                         }
3155                         Update(null);
3156                         PositionCaret(false);
3157                         return false;
3158                      }
3159                   }
3160                   if(sy > caretY)
3161                   {
3162                      curPosition = textBlock.textLen;
3163                      if(!key.shift)
3164                      {
3165                         selPosition = curPosition;
3166                         selBlock = textBlock;
3167                      }
3168                      Update(null);
3169                      PositionCaret(false);
3170                      return false;
3171                   }
3172                   else if(textPos == textBlock.textLen && textBlock.next && textBlock.next.next)
3173                   {
3174                      startPos = 0;
3175                      textPos = 0;
3176                      textBlock = textBlock.next.next;
3177                      sy = textBlock.startY;
3178                      sx = textBlock.startX;
3179                      text = textBlock.text;
3180                   }
3181                   else
3182                   {
3183                      sy += th;
3184                      sx = textBlock.startX;
3185                   }
3186                }
3187
3188                /*if(textBlock.next && textBlock.next.next)
3189                {
3190                   textBlock = textBlock.next.next;
3191                   selPosition = curPosition = Min(curPosition, textBlock.textLen);
3192                   selBlock = textBlock;
3193                   PositionCaret(false);
3194                }*/
3195                break;
3196             }
3197             case Key { right, shift = true, ctrl = true }:
3198             case ctrlRight:
3199             {
3200                bool foundAlpha = false;
3201                bool found = false;
3202                Block line, lastLine;
3203                int lastC;
3204
3205                for(line = textBlock; (line && !found); line = line.next ? line.next.next : null)
3206                {
3207                   int start = (line == textBlock) ? curPosition : 0;
3208                   int c;
3209                   for(c = start; c < line.textLen; c++)
3210                   {
3211                      char ch = line.text[c];
3212                      bool isAlUnder = CharMatchCategories(ch, letters|numbers|marks|connector);
3213                      if(key.shift ? isAlUnder : !isAlUnder)
3214                      {
3215                         foundAlpha = true;
3216                         lastC = c;
3217                         lastLine = line;
3218                      }
3219                      else if(foundAlpha)
3220                      {
3221                         found = true;
3222                         if(!key.shift)
3223                         {
3224                            curPosition = c;
3225                            if(!key.shift)
3226                            {
3227                               selPosition = curPosition;
3228                               selBlock = textBlock;
3229                            }
3230                            Update(null);
3231                            textBlock = line;
3232                            PositionCaret(true);
3233                         }
3234                         break;
3235                      }
3236                   }
3237                   // No next word found,
3238                   if(!found && (c != curPosition || line != textBlock))
3239                   {
3240                      found = true;
3241                      lastLine = line;
3242                      lastC = line.textLen-1;
3243                      if(key.shift)
3244                         break;
3245                      else
3246                      {
3247                         curPosition = line.textLen;
3248                         if(!key.shift)
3249                         {
3250                            selPosition = curPosition;
3251                            selBlock = textBlock;
3252                         }
3253                         Update(null);
3254
3255                         textBlock = line;
3256                         PositionCaret(true);
3257                      }
3258                   }
3259                   if(!key.shift)
3260                      foundAlpha = true;
3261                }
3262                if(key.shift && found)
3263                {
3264                   curPosition = lastC+1;
3265                   textBlock = lastLine;
3266                   PositionCaret(true);
3267                   Update(null);
3268                }
3269                break;
3270             }
3271             case Key { left, ctrl = true, shift = true }:
3272             case ctrlLeft:
3273             {
3274                bool foundAlpha = false;
3275                bool found = false;
3276                Block line, lastLine;
3277                int lastC;
3278
3279                for(line = textBlock; (line && !found); line = line.prev ? line.prev.prev : null)
3280                {
3281                   int start, c;
3282                   if(curPosition == 0 && line != textBlock)
3283                   {
3284                      foundAlpha = true;
3285                      lastC = line.textLen;
3286                      lastLine = line;
3287                      break;
3288                   }
3289                   if(line == textBlock) start = curPosition-1; else start = line.textLen-1;
3290                   for(c = start; c>=0; c--)
3291                   {
3292                      if(CharMatchCategories(line.text[c], letters|numbers|marks|connector))
3293                      {
3294                         foundAlpha = true;
3295                         lastC = c;
3296                         lastLine = line;
3297                      }
3298                      else
3299                      {
3300                         if(foundAlpha)
3301                         {
3302                            found = true;
3303                            break;
3304                         }
3305                      }
3306                   }
3307                   // No next word found,
3308                   if(!found && curPosition > 0)
3309                   {
3310                      foundAlpha = true;
3311                      lastC = 0;
3312                      lastLine = line;
3313                      break;
3314                   }
3315                }
3316                if(foundAlpha)
3317                {
3318                   textBlock = lastLine;
3319                   curPosition = lastC;
3320                   if(!key.shift)
3321                   {
3322                      selPosition = curPosition;
3323                      selBlock = textBlock;
3324                   }
3325                   PositionCaret(true);
3326                   Update(null);
3327                }
3328                break;
3329             }
3330             case Key { right, shift = true }:
3331             case right:
3332                if(curPosition < textBlock.textLen)
3333                {
3334                   curPosition += UTF8_NUM_BYTES(textBlock.text[curPosition]);
3335                   if(!key.shift)
3336                   {
3337                      selPosition = curPosition;
3338                      selBlock = textBlock;
3339                   }
3340                   PositionCaret(true);
3341                   Update(null);
3342                }
3343                else if(textBlock.next && textBlock.next.next)
3344                {
3345                   textBlock = textBlock.next.next;
3346                   curPosition = 0;
3347                   if(!key.shift)
3348                   {
3349                      selPosition = curPosition;
3350                      selBlock = textBlock;
3351                   }
3352                   PositionCaret(true);
3353                   Update(null);
3354                }
3355                break;
3356             case Key { left, shift = true }:
3357             case left:
3358                if(curPosition > 0)
3359                {
3360                   while(curPosition > 0 && !UTF8_IS_FIRST(textBlock.text[--curPosition]));
3361                   if(!key.shift)
3362                   {
3363                      selPosition = curPosition;
3364                      selBlock = textBlock;
3365                   }
3366                   PositionCaret(true);
3367                   Update(null);
3368                }
3369                else if(textBlock.prev)
3370                {
3371                   textBlock = textBlock.prev.prev;
3372                   curPosition = textBlock.textLen;
3373                   if(!key.shift)
3374                   {
3375                      selPosition = curPosition;
3376                      selBlock = textBlock;
3377                   }
3378                   PositionCaret(true);
3379                   Update(null);
3380                }
3381                break;
3382             case backSpace:
3383                if(readOnly) break;
3384                if(textBlock == selBlock && curPosition == selPosition)
3385                {
3386                   if(curPosition)
3387                   {
3388                      int c = curPosition;
3389                      int nb = 1;
3390                      while(c > 0 && !UTF8_IS_FIRST(textBlock.text[--c])) nb++;
3391                      memmove(textBlock.text + curPosition - nb, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3392                      textBlock.textLen -= nb;
3393                      textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
3394                      curPosition -= nb;
3395                      selPosition = curPosition;
3396                      selBlock = textBlock;
3397
3398                      ComputeMinSizes();
3399                      ComputeSizes();
3400                      PositionCaret(true);
3401                      Update(null);
3402                   }
3403                   else if(textBlock.prev)
3404                   {
3405                      Block prev = textBlock.prev, prevBlock = textBlock.prev.prev;
3406                      prevBlock.text = renew prevBlock.text char[prevBlock.textLen + textBlock.textLen + 1];
3407                      memcpy(prevBlock.text + prevBlock.textLen, textBlock.text, textBlock.textLen + 1);
3408
3409                      selPosition = curPosition = prevBlock.textLen;
3410                      selBlock = textBlock;
3411                      prevBlock.textLen += textBlock.textLen;
3412                      textBlock.parent.subBlocks.Remove(prev);
3413                      if(prev == selBlock)
3414                      {
3415                         selBlock = textBlock;
3416                         selPosition = curPosition;
3417                      }
3418                      delete prev;
3419                      textBlock.parent.subBlocks.Remove(textBlock);
3420                      if(textBlock == selBlock)
3421                      {
3422                         selBlock = prevBlock;
3423                         selPosition = curPosition;
3424                      }
3425                      delete textBlock;
3426                      textBlock = prevBlock;
3427
3428                      ComputeMinSizes();
3429                      ComputeSizes();
3430                      PositionCaret(true);
3431                      Update(null);
3432                   }
3433                }
3434                else
3435                   DeleteSelection();
3436                break;
3437             case del:
3438                if(readOnly) break;
3439                if(textBlock != selBlock || curPosition != selPosition)
3440                   DeleteSelection();
3441                else if(textBlock.textLen > curPosition)
3442                {
3443                   int nb = UTF8_NUM_BYTES(textBlock.text[curPosition]);
3444                   memmove(textBlock.text + curPosition, textBlock.text + curPosition + nb, textBlock.textLen - curPosition + 1 - nb + 1);
3445                   textBlock.textLen -= nb;
3446                   textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
3447
3448                   ComputeMinSizes();
3449                   ComputeSizes();
3450
3451                   PositionCaret(true);
3452                   Update(null);
3453                }
3454                else if(textBlock.next && textBlock.next.next)
3455                {
3456                   Block next = textBlock.next, nextBlock = textBlock.next.next;
3457                   textBlock.text = renew textBlock.text char[textBlock.textLen + nextBlock.textLen + 1];
3458                   memcpy(textBlock.text + textBlock.textLen, nextBlock.text, nextBlock.textLen + 1);
3459
3460                   textBlock.textLen += nextBlock.textLen;
3461                   textBlock.parent.subBlocks.Remove(next);
3462                   if(next == selBlock)
3463                   {
3464                      selBlock = textBlock;
3465                      selPosition = curPosition;
3466                   }
3467                   delete next;
3468                   textBlock.parent.subBlocks.Remove(nextBlock);
3469                   if(nextBlock == selBlock)
3470                   {
3471                      selBlock = textBlock;
3472                      selPosition = curPosition;
3473                   }
3474                   delete nextBlock;
3475
3476                   ComputeMinSizes();
3477                   ComputeSizes();
3478                   PositionCaret(true);
3479                   Update(null);
3480                }
3481                break;
3482             case enter:
3483             {
3484                int th = 0;
3485                Block block;
3486                Block newBlock;
3487                int startY, startX;
3488
3489                if(readOnly) break;
3490                DeleteSelection();
3491
3492                block = { type = BR, parent = textBlock.parent, font = textBlock.font };
3493                newBlock = { type = TEXT, parent = textBlock.parent, font = textBlock.font };
3494                startY = textBlock.startY;
3495                startX = textBlock.startX;
3496
3497                display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3498                textBlock.parent.subBlocks.Insert(textBlock, block);
3499                textBlock.parent.subBlocks.Insert(block, newBlock);
3500
3501                startY += th;
3502
3503                newBlock.textLen = textBlock.textLen - curPosition;
3504                newBlock.text = new char[newBlock.textLen+1];
3505                memcpy(newBlock.text, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3506                textBlock.textLen = curPosition;
3507                textBlock.text[curPosition] = 0;
3508
3509                newBlock.startY = startY;
3510                newBlock.startX = startX;
3511                selPosition = curPosition = 0;
3512
3513                ComputeMinSizes();
3514                ComputeSizes();
3515
3516                textBlock = newBlock;
3517                selBlock = textBlock;
3518                PositionCaret(true);
3519                Update(null);
3520                break;
3521             }
3522             case ctrlX:
3523             case Key { del, shift = true }:
3524                if(readOnly) break;
3525                // Cut
3526                CopySelection();
3527                DeleteSelection();
3528                break;
3529             case ctrlC:
3530             case ctrlInsert:
3531                // Copy
3532                CopySelection();
3533                break;
3534             case shiftInsert:
3535             case ctrlV:
3536                if(!readOnly)
3537                {
3538                   ClipBoard clipBoard { };
3539                   if(clipBoard.Load())
3540                   {
3541                      int c;
3542                      char * text = clipBoard.memory;
3543                      char ch;
3544                      int start = 0;
3545                      Block parent;
3546                      FontEntry font;
3547
3548                      DeleteSelection();
3549
3550                      parent = textBlock.parent;
3551                      font = textBlock.font;
3552
3553                      for(c = 0; ; c++)
3554                      {
3555                         ch = text[c];
3556                         if(ch == '\n' || ch == '\r' || !ch)
3557                         {
3558                            int len = c - start;
3559                            textBlock.text = renew textBlock.text char[textBlock.textLen + 1 + len];
3560                            memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3561                            memcpy(textBlock.text + curPosition, text + start, len);
3562                            textBlock.textLen += len;
3563                            curPosition += len;
3564                            selPosition = curPosition;
3565                            selBlock = textBlock;
3566                            if(!ch) break;
3567                            {
3568                               Block block { type = BR, parent = parent, font = font };
3569                               Block newBlock { type = TEXT, parent = parent, font = font };
3570                               int startY = textBlock.startY, startX = textBlock.startX;
3571                               int th = 0;
3572
3573                               display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3574                               textBlock.parent.subBlocks.Insert(textBlock, block);
3575                               textBlock.parent.subBlocks.Insert(block, newBlock);
3576
3577                               startY += th;
3578
3579                               newBlock.textLen = textBlock.textLen - curPosition;
3580                               newBlock.text = new char[newBlock.textLen+1];
3581                               memcpy(newBlock.text, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3582                               textBlock.textLen = curPosition;
3583                               textBlock.text[curPosition] = 0;
3584
3585                               newBlock.startY = startY;
3586                               newBlock.startX = startX;
3587                               selPosition = curPosition = 0;
3588                               selBlock = textBlock;
3589                               textBlock = newBlock;
3590                            }
3591                            if(ch == '\r' && text[c+1] == '\n') c++;
3592                            start = c + 1;
3593                         }
3594                      }
3595                      ComputeMinSizes();
3596                      ComputeSizes();
3597                      PositionCaret(true);
3598                      Update(null);
3599                   }
3600                   delete clipBoard;
3601                }
3602                break;
3603             default:
3604             {
3605                // eC BUG HERE: (Should be fixed)
3606                if(!readOnly && !key.ctrl && !key.alt && ch >= 32 && ch != 128 /*&& ch < 128*/)
3607                {
3608                   char string[5];
3609                   int len = UTF32toUTF8Len(&ch, 1, string, 5);
3610                   int c;
3611
3612                   DeleteSelection();
3613
3614                   textBlock.text = renew textBlock.text char[textBlock.textLen + len + 1];
3615                   memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
3616
3617                   for(c = 0; c<len; c++)
3618                   {
3619                      textBlock.text[curPosition] = string[c];
3620                      textBlock.textLen++;
3621                      curPosition++;
3622                   }
3623                   selPosition = curPosition;
3624                   selBlock = textBlock;
3625
3626                   {
3627                      //Clear(html.block);
3628                      //CreateForms(html.block);
3629                      ComputeMinSizes();
3630                      ComputeSizes();
3631                      //PositionForms();
3632                   }
3633                   PositionCaret(true);
3634                   Update(null);
3635                }
3636             }
3637          }
3638       }
3639       return true;
3640    }
3641
3642    void OnResize(int width, int height)
3643    {
3644       HTMLView::OnResize(width, height);
3645       PositionCaret(true);
3646    }
3647
3648    int caretX, caretY;
3649    void PositionCaret(bool setCaretX)
3650    {
3651       if(edit)
3652       {
3653          int tw = 0, th = 0;
3654          int textPos = 0;
3655          int sx = textBlock.startX, sy = textBlock.startY;
3656          char * text = textBlock.text;
3657          int maxW;
3658          Block block = textBlock;
3659          while(block && block.type != TD) block = block.parent;
3660          if(block)
3661          {
3662             Block table = block;
3663             while(table && table.type != TABLE) table = table.parent;
3664             if(table)
3665                maxW = block.w - 2* table.cellPadding;
3666             else
3667                maxW = clientSize.w - 10 - sx;
3668          }
3669          else
3670             maxW = clientSize.w - 10 - sx;
3671
3672          display.FontExtent(textBlock.font.font, " ", 1, null, &th);
3673
3674          while(textPos < textBlock.textLen)
3675          {
3676             int startPos = textPos;
3677             int width = 0;
3678             int x = 0;
3679             bool lineComplete = false;
3680
3681             for(; textPos<textBlock.textLen && !lineComplete;)
3682             {
3683                int w;
3684                int len;
3685                char * nextSpace = strchr(text + textPos, ' ');
3686
3687                if(nextSpace)
3688                   len = (nextSpace - (text + textPos)) + 1;
3689                else
3690                   len = textBlock.textLen - textPos;
3691
3692                display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
3693
3694                if(x + width + w > maxW && x > 0)
3695                {
3696                   lineComplete = true;
3697                   break;
3698                }
3699                textPos += len;
3700
3701                width += w;
3702
3703                if(nextSpace)
3704                {
3705                   x += width;
3706                   width = 0;
3707                }
3708             }
3709             if(curPosition < textPos || textPos == textBlock.textLen)
3710             {
3711                int len = curPosition - startPos;
3712                display.FontExtent(textBlock.font.font, text + startPos, len, &tw, null);
3713                sx += tw;
3714                break;
3715             }
3716             sy += th;
3717             sx = textBlock.startX;
3718          }
3719          if(setCaretX)
3720             caretX = sx;
3721          caretY = sy;
3722          SetCaret(sx, sy, th);
3723          {
3724             Point scrollPos = scroll;
3725             bool doScroll = false;
3726             if(sy - scroll.y + th > clientSize.h)
3727             {
3728                scrollPos.y = sy + th - clientSize.h;
3729                doScroll = true;
3730             }
3731             else if(sy - scroll.y < 0)
3732             {
3733                scrollPos.y = sy;
3734                doScroll = true;
3735             }
3736             if(sx - scroll.x + 10 > clientSize.w)
3737             {
3738                scrollPos.x = sx + 10 - clientSize.w;
3739                doScroll = true;
3740             }
3741             else if(sx - scroll.x < 10)
3742             {
3743                scrollPos.x = sx - 10;
3744                doScroll = true;
3745             }
3746             if(doScroll)
3747                scroll = scrollPos;
3748          }
3749       }
3750       else
3751          SetCaret(0,0,0);
3752    }
3753
3754    // Returns a character offset into the TextBlock from a window coordinate
3755    int TextPosFromPoint(int px, int py, Block * block, bool half)
3756    {
3757       Block parentBlock = this.textBlock.parent;
3758       Block textBlock;
3759       int result = 0;
3760       *block = this.textBlock;
3761
3762       px += scroll.x;
3763       py += scroll.y;
3764
3765       for(textBlock = parentBlock.subBlocks.first; textBlock; textBlock = textBlock.next)
3766       {
3767          int sx = textBlock.startX, sy = textBlock.startY;
3768          int th = 0;
3769          int textPos = 0;
3770          char * text = textBlock.text;
3771          int maxW;
3772          Block b = textBlock;
3773          int space;
3774
3775          if(textBlock.type != TEXT) continue;
3776
3777          while(b && b.type != TD) b = b.parent;
3778          if(b)
3779          {
3780             Block table = b;
3781             while(table && table.type != TABLE) table = table.parent;
3782             if(table)
3783                maxW = b.w - 2* table.cellPadding;
3784             else
3785                maxW = clientSize.w - 10 - sx;
3786          }
3787          else
3788             maxW = clientSize.w - 10 - sx;
3789
3790          display.FontExtent(textBlock.font.font, " ", 1, &space, &th);
3791          //space = space/2+2;
3792          space = 2;
3793
3794          while(textPos < textBlock.textLen)
3795          {
3796             int width = 0;
3797             int x = 0;
3798             bool lineComplete = false;
3799
3800             for(; textPos<textBlock.textLen && !lineComplete;)
3801             {
3802                int w;
3803                int len;
3804                char * nextSpace = strchr(text + textPos, ' ');
3805
3806                if(nextSpace)
3807                   len = (nextSpace - (text + textPos)) + 1;
3808                else
3809                   len = textBlock.textLen - textPos;
3810
3811                display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th);
3812
3813                sx = x + textBlock.startX;
3814                if(/*py >= sy && */py < sy + th && /*px >= sx-space && */px < sx + w-space)
3815                {
3816                   int c, numBytes;
3817                   char ch;
3818                   *block = textBlock;
3819                   for(c = textPos; (ch = text[c]); c += numBytes)
3820                   {
3821                      numBytes = UTF8_NUM_BYTES(ch);
3822                      display.FontExtent(textBlock.font.font, text + c, numBytes, &w, &th);
3823                      if(/*py >= sy && */py < sy + th && /*px >= sx-w/2-space && */px < sx + (half ? w/2 : w) -space)
3824                         break;
3825                      sx += w;
3826                   }
3827                   return c;
3828                }
3829
3830                if(x + width + w > maxW && x > 0)
3831                {
3832                   lineComplete = true;
3833                   break;
3834                }
3835                textPos += len;
3836
3837                width += w;
3838
3839                if(nextSpace)
3840                {
3841                   x += width;
3842                   width = 0;
3843                }
3844             }
3845             if(/*py >= sy && */py < sy + th)
3846             {
3847                *block = textBlock;
3848                return textBlock.textLen;
3849             }
3850             sy += th;
3851          }
3852          *block = textBlock;
3853          result = textBlock.textLen;
3854       }
3855       return result;
3856    }
3857 }
3858
3859 Application componentsApp;
3860
3861 class Documentor : GuiApplication
3862 {
3863    bool Init()
3864    {
3865       Platform os = __runtimePlatform;
3866       componentsApp = __ecere_COM_Initialize(false, 1, null);
3867       SetPrivateModule(componentsApp);
3868       SetGlobalContext(globalContext);
3869       SetExcludedSymbols(&excludedSymbols);
3870       SetDefines(&::defines);
3871       SetImports(&imports);
3872
3873       SetGlobalData(globalData);
3874
3875       settingsContainer.dataOwner = &settings;
3876       settingsContainer.Load();
3877       if(!settings.docDir || !settings.docDir[0] )
3878       {
3879          if(os == win32) // if Windows OS then
3880          {
3881             char programFilesDir[MAX_LOCATION];
3882             char appData[MAX_LOCATION];
3883             char homeDrive[MAX_LOCATION];
3884             char winDir[MAX_LOCATION];
3885             GetEnvironment("APPDATA", appData, sizeof(appData));
3886             GetEnvironment("HOMEDRIVE", homeDrive, sizeof(homeDrive));
3887             GetEnvironment("windir", winDir, sizeof(winDir));
3888             if(GetEnvironment("ProgramFiles", programFilesDir, MAX_LOCATION))
3889             {
3890                PathCat(programFilesDir, "ECERE SDK\\doc");
3891                settings.docDir = programFilesDir;
3892             }
3893             else if(homeDrive[0])
3894             {
3895                PathCat(homeDrive, "ECERE SDK\\doc");
3896                settings.docDir = homeDrive;
3897             }
3898             else if(winDir[0])
3899             {
3900                PathCat(winDir, "..\\ECERE SDK\\doc");
3901                settings.docDir = winDir;
3902             }
3903             else
3904                settings.docDir = "C:\\ECERE SDK\\doc";
3905          }
3906          else // if Os is Linux, or Mac OSX or something else
3907             settings.docDir = "/usr/share/ecere/doc/";
3908          settingsContainer.Save();
3909       }
3910
3911       //if(argc > 1)
3912       {
3913       #if 0
3914          Module module = eModule_Load(componentsApp, "ecere" /*argv[1]*/, privateAccess);
3915          DataRow row;
3916          AddComponents(module, true);
3917          mainForm.browser.currentRow = row = mainForm.browser.FindSubRow((int64)module);
3918          // mainForm.browser.currentRow = row = mainForm.browser.FindSubRow((int64)eSystem_FindClass(componentsApp, "Window"));
3919          while((row = row.parent))
3920             row.collapsed = false;
3921       #endif
3922       }
3923
3924       commandThread.Create();
3925       return true;
3926    }
3927
3928    bool Cycle(bool idle)
3929    {
3930       if(quit)
3931          mainForm.Destroy(0);
3932       return true;
3933    }
3934
3935    void Terminate()
3936    {
3937       PrintLn("Exited");
3938       console.Flush();
3939       quit = true;
3940       if(commandThread.created)
3941       {
3942          console.CloseInput();
3943          console.CloseOutput();
3944          app.Unlock();
3945          commandThread.Wait();
3946          app.Lock();
3947       }
3948
3949       FreeContext(globalContext);
3950       FreeExcludedSymbols(excludedSymbols);
3951       ::defines.Free(FreeModuleDefine);
3952       imports.Free(FreeModuleImport);
3953
3954       FreeGlobalData(globalData);
3955       FreeTypeData(componentsApp);
3956       FreeIncludeFiles();
3957       delete componentsApp;
3958    }
3959 }
3960
3961 ConsoleFile console { };
3962 MainForm mainForm { };
3963 bool quit;
3964
3965 Thread commandThread
3966 {
3967    unsigned int Main()
3968    {
3969       while(!quit)
3970       {
3971          char command[1024];
3972          console.GetLine(command, sizeof(command));
3973          if(!quit && command[0])
3974          {
3975             app.Lock();
3976             if(!strcmpi(command, "Activate"))
3977                mainForm.Activate();
3978             else if(!strcmpi(command, "Quit"))
3979                quit = true;
3980             app.Unlock();
3981          }
3982       }
3983       return 0;
3984    }
3985 };