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