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