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