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