installer: (#781) Fixed setting documentor API ref path; fixed broken default project...
[sdk] / installer / src / installer.ec
1 #ifdef NOMINGW
2 static define buildString = $"Ecere SDK v0.44.08 (Without MinGW) -- built on August 8, 2013 ";
3 #else
4 static define buildString = $"Ecere SDK v0.44.08 -- built on August 8, 2013 ";
5 #endif
6
7 #define WIN32_LEAN_AND_MEAN
8 #define GetFreeSpace _GetFreeSpace
9 #include <windows.h>
10 #undef GetFreeSpace
11
12 #ifdef ECERE_STATIC
13 import static "ecere"
14 #else
15 import "ecere"
16 #endif
17 import "IDESettings"
18 import "createLink"
19 import "licensing"
20 import "CheckListBox"
21
22 struct CheckItem
23 {
24    char * name;
25    void * data;
26    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
27    {
28       return name;
29    }
30
31    void OnDisplay(Surface surface, int x, int y, int width, void * fieldData, Alignment alignment, DataDisplayFlags displayFlags)
32    {
33       if(!displayFlags.active) { displayFlags.current = false; displayFlags.selected = false; }
34       class::OnDisplay(surface, x + 22, y, width - 22, fieldData, alignment, displayFlags);
35    }
36 };
37
38 //#define BUFFERSIZE 0x10000
39 #define BUFFERSIZE 0x1000
40 bool abortInstall = false;
41
42 void ExtractFileFromArchive(ProgressBar progressBar, char * path, char * outputFile)
43 {
44    char fileName[MAX_LOCATION];
45    FileAttribs exists = FileExists(path);
46    bool setTime = false;
47    FileStats stats;
48
49    if(exists.isDirectory)
50    {
51       FileListing listing { path };
52
53       if(outputFile[0])
54       {
55          if(MakeDir(outputFile))
56          {
57             setTime = true;
58             FileGetStats(path, &stats);
59          }
60       }
61
62       while(listing.Find() && !abortInstall)
63       {
64          strcpy(fileName, outputFile);
65
66          // Tweak file name if out
67          if(outputFile[0])
68          {
69             if(!strcmp(fileName, ".")) fileName[0] = '\0';
70             if(listing.name[0] == '/' || listing.name[0] == '\\')
71             {
72                char * afterSlash, rest[MAX_LOCATION];
73                for(afterSlash = fileName; *afterSlash == '/' || *afterSlash == '\\'; afterSlash++);
74                strcpy(rest, afterSlash);
75                PathCat(fileName, "_root");
76                PathCat(fileName, rest);
77             }
78             else if(listing.name[1] == ':')
79             {
80                char letter[10];
81                sprintf(letter, "_%cdrive", toupper(listing.name[0]));
82                PathCat(fileName, letter);
83                PathCat(fileName, listing.name[2] ? (listing.name + 3) : (listing.name + 2));
84             }
85             else
86                PathCat(fileName, listing.name);
87          }
88          else
89             PathCat(fileName, listing.name);
90          if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
91             strcpy(fileName, DIR_SEPS);
92          ExtractFileFromArchive(progressBar, listing.path, fileName);
93       }
94       listing.Stop();
95    }
96    else if(exists)
97    {
98       File input = FileOpen(path, read);
99       if(input)
100       {
101          File output = FileOpen(outputFile, write);
102          if(output)
103          {
104             char fileName[MAX_FILENAME];
105             uint position = progressBar.progress;
106             FileSize dataSize;
107             int c;
108             byte buffer[BUFFERSIZE];
109
110             FileGetSize(path, &dataSize);
111             GetLastDirectory(outputFile, fileName);
112             
113             ((GuiApplication)__thisModule).SignalEvent();
114             //((GuiApplication)__thisModule).ProcessInput();
115             //((GuiApplication)__thisModule).UpdateDisplay();
116             
117             for(c = 0; c<dataSize && !abortInstall; c += BUFFERSIZE)
118             {
119                uint size = (dataSize > c + BUFFERSIZE) ? BUFFERSIZE : (dataSize - c);
120
121                ((GuiApplication)__thisModule).Unlock();
122
123                input.Read(buffer, 1, size);
124                output.Write(buffer, 1, size);
125
126                ((GuiApplication)__thisModule).Lock();
127
128                progressBar.progress = position + c + size;
129                ((GuiApplication)__thisModule).SignalEvent();
130
131                //((GuiApplication)__thisModule).ProcessInput();
132                //((GuiApplication)__thisModule).UpdateDisplay();
133             }
134
135             delete output;
136             setTime = true;
137             FileGetStats(path, &stats);
138          }
139          delete input;
140       }
141    }
142    if(setTime)
143       FileSetTime(outputFile, stats.created, 0, stats.modified);
144 }
145
146 struct Component
147 {
148    char * name;
149    char * dataPath;
150    char * defInstallPath;
151    Component * subComponents;
152    bool mandatory;
153    bool selected;
154    Component * parent;
155
156    uint requiredSize;
157    uint size;
158    char installPath[MAX_LOCATION];
159    DataRow row;
160
161    void GetFullPath(char * path)
162    {
163       if(this != null && parent)
164          parent->GetFullPath(path);
165       else
166          strcpy(path, installDir);
167
168       if(this != null)
169          PathCat(path, installPath);
170    }
171
172    void Install(char * parentPath)
173    {
174       int c;
175       if(selected)
176       {
177          char path[MAX_LOCATION];
178          strcpy(path, parentPath);
179          PathCat(path, installPath);
180
181          installProgress.installing.SetText($"Installing %s...", name);
182          ((GuiApplication)__thisModule).UpdateDisplay();
183
184          if(dataPath)
185          {
186             char source[MAX_LOCATION];
187             strcpy(source, ":");
188             strcat(source, dataPath);
189
190             MakeDir(path);
191
192             if(FileExists(source).isFile)
193             {
194                char name[MAX_FILENAME];
195                GetLastDirectory(source, name);
196                PathCat(path, name);
197             }
198             if(requiredSize)
199             {
200                uint p = installProgress.progressBar.progress;
201                ExtractFileFromArchive(installProgress.progressBar, source, path); 
202             }
203          }
204          if(subComponents)
205          {
206             for(c = 0; subComponents[c].name; c++)
207                subComponents[c].Install(path);
208          }
209       }
210    }
211 };
212 Component samples[] =
213 {
214    { "Ecere Chess",     "samples/chess",     "chess",       null, false, true },
215    { "Ecere Fractals",  "samples/fractals",  "fractals",    null, false, true },
216    { "3D",              "samples/3D",        "3D",          null, false, true },
217    { "Audio",           "samples/audio",     "audio",       null, false, true },
218    { "Database",        "samples/db",        "db",          null, false, true },
219    { "eC",              "samples/eC",        "eC",          null, false, true },
220    { "Games",           "samples/games",     "games",       null, false, true },
221    { "GUI & Graphics",  "samples/guiAndGfx", "guiAndGfx",   null, false, true },
222    { "Miscellaneous",   "samples/misc",      "misc",        null, false, true },
223    { "Networking",      "samples/net",       "net",         null, false, true },
224    { "WIA Scanning",    "samples/scanning",  "scanning",    null, false, true },
225    { "Threading",       "samples/threads",   "threads",     null, false, true }
226    { null }
227 };
228
229 public enum CoreSDKID
230 {
231    ide, runtime, ec, eda, vanilla, extras,
232 #ifndef NOMINGW
233    gcc, gdb, mingw, binutils, make,
234 #endif
235    none
236 };
237
238 Component coreSDK[CoreSDKID] =
239 {
240    { "Ecere IDE",       "ecere-sdk/ide",                 "bin",      null, true,  true },
241    { "Runtime Library", "ecere-sdk/ecere.dll",           "bin",      null, true,  true },
242    { "eC Compiler",     "ecere-sdk/compiler",            "bin",      null, true,  true },
243 #ifndef NOMINGW
244    { "GNU C Compiler",  "mingw/gcc/core",   "mingw", null, true, true },
245    { "GNU Debugger",    "mingw/gdb",        "mingw", null, true, true },
246    { "MinGW Runtime",   "mingw/mingwrt",    "mingw", null, true, true },
247    { "Binary Utils",    "mingw/binutils",   "mingw", null, true, true },
248    { "GNU Make",        "mingw/make",       "mingw", null, true, true },
249 #endif
250    { null }
251 };
252
253 #ifndef NOMINGW
254 Component additional[] =
255 {
256    { "Data Access",     "ecere-sdk/eda",                 "bin",      null, false, true },
257    { "Ecere Vanilla",   "ecere-sdk/libecereVanilla.a",   "lib",      null, false, true },
258    { "Ecere Extras",    "extras",                        "extras",   null, false, true },
259    { "UPX",             "upx/bin",                       "mingw/bin",null, false, true },
260    { "GNU Regexp",      "mingw/gnurx",                   "mingw",    null, false, true },
261    { "pthreads",        "mingw/pthreads",                "mingw",    null, false, true },
262    { "C++ Compiler",    "mingw/gcc/c++",                 "mingw",    null, false, true },
263    { "Win32 APIs",      "mingw/w32api",                  "mingw",    null, false, true },
264    { "GCC I18n",        "mingw/locale/gcc",              "mingw",    null, false, false },
265    { "GDB I18n",        "mingw/locale/gdb",              "mingw",    null, false, false },
266    { "Make I18n",       "mingw/locale/make",             "mingw",    null, false, false },
267    { "Binutils I18n",   "mingw/locale/binutils",         "mingw",    null, false, false },
268    { null }
269 };
270 #endif
271
272 public enum DocumentationID
273 {
274    ecereBook, apiRef, tutorials, coursework,
275 #ifndef NOMINGW
276    gccDoc, gppDocs, gdbDocs, makeDocs, binDocs, mingwDocs, gnurxDocs, upxDocs,
277 #endif
278    none
279 };
280
281 Component documentation[DocumentationID] =
282 {
283    { "Ecere Book",         "ecere-sdk/book",       "doc",            null, false, true },
284    { "API Reference",      "ecere-sdk/doc",        "doc",            null, false, true },
285    { "Ecere Tutorials",    "ecere-sdk/tutorials",  "doc",            null, false, true },
286    { "Ecere Coursework",   "ecere-sdk/coursework", "doc",            null, false, true },
287 #ifndef NOMINGW
288    { "GCC Docs",        "mingw/doc/gcc",        "mingw",          null, false, false },
289    { "G++ Docs",        "mingw/doc/g++",        "mingw",          null, false, false },
290    { "GDB Docs",        "mingw/doc/gdb",        "mingw",          null, false, false },
291    { "Make Docs",       "mingw/doc/make",       "mingw",          null, false, false },
292    { "Binutils Docs",   "mingw/doc/binutils",   "mingw",          null, false, false },
293    { "MinGW Docs",      "mingw/doc/mingwrt",    "mingw",          null, false, false },
294    { "gnurx Docs",      "mingw/doc/gnurx",      "mingw",          null, false, false },
295    { "UPX Docs",        "upx/doc",              "mingw/doc/upx",  null, false, false },
296 #endif
297    { null }
298 };
299
300 public enum ComponentID
301 {
302    coreSDK,
303 #ifndef NOMINGW
304    additional,
305 #endif
306    documentation,
307    samples,
308    none
309 };
310
311 Component components[ComponentID] =
312 {
313    { "Core SDK Files", null, null, coreSDK, true, true },
314 #ifndef NOMINGW
315    { "Additional Support", null, null, additional, false, true },
316 #endif
317    { "Documentation", null /*"doc"*/, null /*"doc"*/, documentation, false, true },
318    { "Samples", null, "samples", samples, false, true },
319    { null }
320 };
321 FileSize totalSize;
322 FileSize totalInstalled;
323
324 struct InstallOption
325 {
326    char * name;
327    InstallOption * subOptions;
328    bool selected;
329    DataRow row;
330 };
331
332 enum AssociateOptions
333 {
334    AssociateEPJ,
335    AssociateEC,
336    AssociateC,
337    AssociateCPP,
338    AssociateTXT,
339    Associate3DS,
340    AssociateIMG
341 };
342
343 InstallOption associateOptions[] =
344 {
345    { "Associate with Ecere Project Files (*.epj)", null, true },
346    { "Associate with eC Files (*.ec, *.eh)", null, true },
347    { "Associate with C files (*.c, *.h)", null, false },
348    { "Associate with C++ Files (*.cpp, *.hpp, *.cc, *.hh, *.cxx, *.hxx)", null, false },
349    { "Associate with text files (*.txt)", null, false },
350    { "Associate with 3D Studio Model Files (*.3ds)", null, true },
351    { "Associate with Image Files (*.png, *.jpg, *.pcx, *.bmp, *.gif)", null, false },
352    { null }
353 };
354
355 enum PathOptions
356 {
357    AddECEREPaths
358 #ifndef NOMINGW
359    , AddMinGWPaths
360 #endif
361 };
362
363 InstallOption pathOptions[] =
364 {
365    { "Add Ecere binaries location to the system path", null, true },
366    { "Add MinGW to the system path", null, true }
367    { null }
368 };
369
370 enum IconOptions
371 {
372    StartMenuIcon,
373    DesktopIcon,
374    QuickLaunchIcon
375 };
376
377 InstallOption options[] =
378 {
379    { "Start Menu Group", null, true },
380    { "Desktop Icon", null, true },
381    { "Quicklaunch Icon", null, true },
382    { "Associate the ECERE IDE with Supported File Types", associateOptions, true },
383 #ifndef NOMINGW
384    { "Add binaries location to the system paths", pathOptions, true },
385 #endif
386    { null }
387 };
388
389 char sourceDir[MAX_LOCATION] = ":";
390 char installDir[MAX_LOCATION];
391
392 class Installer : Window
393 {
394    text = $"Ecere Software Development Kit Setup - v0.44.08 \"Ryōan-ji\"";
395    background = activeBorder;
396    borderStyle = fixed;
397    hasMinimize = true;
398    hasClose = true;
399    tabCycle = true;
400    clientSize = { 636, 456 };
401    // clientSize = { 796, 576 };
402    icon = { ":icon.png" };
403
404    Picture back { image = BitmapResource { ":ryoanji.png" }, parent = this, position = { 0, 0 } };
405    FileDialog fileDialog
406    {
407       master = this, type = selectDir,
408       text = $"Select a new location"
409    };
410    Button browse
411    {
412       master = this, autoCreate = false, inactive = true, text = "...";
413       
414       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
415       {
416          DataRow row = componentsBox.currentRow;
417          Component * component = ((CheckItem *)row.GetData(componentField))->data;
418          component->GetFullPath(fileDialog.filePath);
419          StripLastDirectory(fileDialog.filePath, fileDialog.currentDirectory);
420
421          if(fileDialog.Modal() == ok)
422          {
423             componentsBox.StopEditing(false);
424             row.SetData(locationField, fileDialog.filePath);
425             componentsBox.NotifyChanged(this, componentsBox, row);
426             componentsBox.currentField = componentsBox.currentField;
427          }
428          return true;
429       }
430    };
431    CheckListBox componentsBox
432    {
433       this, size = { 460, 112 }, position = { 160, 160 }, hasHeader = true;
434       fullRowSelect = false, collapseControl = true, treeBranches = true, rootCollapseButton = true, 
435       noDragging = true;
436       rowHeight = 18;
437       selectionColor = { 145, 150, 140 };
438
439       alwaysEdit = true;
440       opacity = 0;
441
442       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
443       {
444          if(key == f2 && browse.visible)
445             browse.NotifyClicked(this, browse, 0, 0, 0);
446          return true;
447       }
448
449       bool NotifyChanged(ListBox listBox, DataRow row)
450       {
451          Component * component = ((CheckItem *)listBox.GetData(componentField))->data;
452          char path[MAX_LOCATION], relative[MAX_LOCATION] = "", * newPath;
453          char fullPath[MAX_LOCATION];
454
455          component->parent->GetFullPath(path);
456          strcpy(fullPath, path);
457
458          newPath = row.GetData(locationField);
459          if(newPath)
460          {
461             PathCat(fullPath, newPath);
462             MakePathRelative(fullPath, path, relative);
463          }
464          listBox.SetData(locationField, relative);
465          strcpy(component->installPath, relative);
466
467          //SetAvailableSpace(component, path);
468          {
469             ComponentID c;
470             install.disabled = false;
471             for(c = 0; components[c].name; c++)
472             {
473                SetAvailableSpace(components[c], installDir);
474             }
475          }
476          return true;
477       }
478
479       bool NotifyEditDone(ListBox listBox, DataRow row)
480       {
481          browse.Destroy(0);
482          return true;
483       }
484
485       bool NotifyEdited(ListBox listBox, DataRow row)
486       {
487          Window e;
488          browse.parent = listBox;
489          browse.position = { componentField.width + locationField.width + 18, (listBox.currentIndex+1) * listBox.rowHeight - 2 };
490          browse.size = { 30, listBox.rowHeight + 3 };
491          for(e = listBox.firstChild; e; e = e.next)
492          {
493             if(e._class == class(DataBox))
494                break;
495          }
496          if(e)
497             e.Activate();
498          browse.Create();
499          return true;
500       }
501
502       void NotifyChecked(CheckListBox listBox, DataRow row)
503       {
504          Component * component = ((CheckItem *)row.GetData(componentField))->data;
505          int c;
506          component->selected = listBox.IsChecked(row);
507
508          if(!component->parent) totalSize -= component->requiredSize;
509          component->requiredSize = 0;
510          if(component->selected)
511          {
512             component->requiredSize += component->size; 
513             if(component->subComponents)
514                for(c = 0; component->subComponents[c].name; c++)
515                   component->requiredSize += component->subComponents[c].requiredSize;
516          }
517          if(component->requiredSize)
518          {
519             uint requiredSize = component->requiredSize;
520             row.SetData(reqField, requiredSize);
521          }
522          else
523             row.UnsetData(reqField);
524          if(!component->parent) 
525          {
526             totalSize += component->requiredSize;
527             {
528                char sizeString[100];
529                PrintSize(sizeString, totalSize, 2);
530                totalSpaceValue.text = sizeString;
531             }
532          }
533       }
534    };
535    Label agreementLbl { parent = this, text = $"By installing the Ecere SDK, you agree to the                                         .", font = { "Tahoma", 8.25f }, anchor = Anchor { left = 24, top = 424 } };
536    Button licenseButton
537    {
538       this, inactive = true, offset = false, bevel = false, foreground = blue, font = { "Tahoma", 8.25f, underline = true, bold = true },
539       // text = $"terms and conditions", anchor = Anchor { left = 241, top = 421 };
540       text = $"terms and conditions", anchor = Anchor { left = 237, top = 421 };
541       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
542
543       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
544       {
545          // LicenseBox { master = this, sourceFile = ":ecere-sdk/doc/LICENSE" }.Modal();
546          LicensesForm { master = this }.Modal();
547          return true;
548       }
549    };
550    CheckListBox optionsBox
551    {
552       this, size = { 460, 94 }, position = { 160, 284 };
553       fullRowSelect = false, collapseControl = true, treeBranches = true, rootCollapseButton = true, 
554       noDragging = true;
555       rowHeight = 18;
556       opacity = 0;
557
558       void NotifyChecked(CheckListBox listBox, DataRow row)
559       {
560          CheckItem * item = row.GetData(optionField);
561          InstallOption * option = item->data;
562          option->selected = listBox.IsChecked(row);
563       }
564    };
565    Button install
566    {
567       parent = this, text = $"Install", isDefault = true, size = { 75, 23 }, position = { 432, 416 };
568
569       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
570       {
571          installProgress.Create();
572          Destroy(0);
573          // installProgress.thread.Main();
574          installProgress.thread.Create();
575          return true;
576       }
577    };
578    Button button3 { parent = this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 }, NotifyClicked = ButtonCloseDialog };
579    Label label1 { labeledWindow = destBox, tabCycle = true, isGroupBox = true, parent = this, inactive = false, size = Size { 458, 50 }, anchor = Anchor { left = 160, top = 96 } };
580    PathBox destBox
581    {
582       parent = label1, master = this, text = $" Destination Folder", size = Size { 336, 22 }, anchor = Anchor { left = 12, top = 20, right = 12 };
583       typeExpected = directory;
584       browseDialog = fileDialog;
585       opacity = 0;
586
587       bool NotifyModified(PathBox pathBox)
588       {
589          ComponentID c;
590          strcpy(installDir, destBox.path);
591          install.disabled = false;
592          for(c = 0; components[c].name; c++)
593          {
594             SetAvailableSpace(components[c], installDir);
595          }
596          return true;
597       }
598    };
599    /*Button button1
600    {
601       label1, this, $"Browse", altB, size = { 83, 24 }, position = { 360, 16 };
602
603       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
604       {
605          strcpy(fileDialog.filePath, installDir);
606          StripLastDirectory(installDir, fileDialog.currentDirectory);
607          if(fileDialog.Modal())
608          {
609             strcpy(installDir, fileDialog.filePath);
610             destBox.contents = installDir;
611             // TOCHECK: Should setting contents call NotifyModified?
612             destBox.NotifyModified(destBox.master, destBox);
613          }
614          return true;
615       }
616    };*/
617    EditBox label5
618    {
619       this, multiLine = true,
620       opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 96 }, noSelect = true,
621       contents = $"Select the default root\n"
622          "folder where to install\n"
623          "all components:"
624    };
625    EditBox label6
626    {
627       this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 132 }, position = { 14, 152 }, noSelect = true,
628       multiLine = true,
629       contents = $"Select the optional\n"
630          "components you wish\n"
631          "to install:\n\n"
632          "You may customize the\n"
633          "install location for each\n"
634          "of them, or use the default\n"
635          "based on the main\n"
636          "destination folder."
637    };
638    EditBox label7
639    {
640       this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 280 }, noSelect = true, 
641       multiLine = true,
642       contents = $"Select icons to install, file\n"
643       "associations, and system\n"
644       "environment modifications:"
645    };
646    Label totalSpaceLabel
647    {
648       this, position = { 18, 352 }, text = $"Space Required: "
649    };
650    Label totalSpaceValue
651    {
652       this, position = { 100, 352 }, text = "0 mb"
653    };
654    EditBox editBox1
655    {
656       inactive = true, noSelect = true,
657       multiLine = true, parent = label3, text = "editBox1", opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
658       contents = $"Choose in which folder to install the ECERE SDK, which features\n"
659          "of the SDK to install, as well as where to install program icons."
660    };
661    Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
662    Picture picture1
663    {
664       image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, text = "picture1", anchor = Anchor { left = 16, top = 4 };
665       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
666
667       bool OnLeftButtonDown(int x, int y, Modifiers mods)
668       {
669          ShellOpen("http://www.ecere.com/");
670          return true;
671       }
672    };
673    Label label4 { parent = label3, text = $"Choose Components, Locations and Install Options", font = FontResource { "Tahoma", 8.25f, bold = true }, size = Size { 326, 16 }, anchor = Anchor { horz = 91, vert = -12 } };
674    DataField componentField { "CheckItem", width = 140, header = $"Component" };
675    DataField locationField { "char *", width = 108, header = $"Destination Folder", editable = true };
676    DataField reqField { "FileSize", width = 80, header = $"Req. Space", alignment = right };
677    DataField avField { "FileSize64", width = 80, header = $"Avail. Space", alignment = right };
678    DataField optionField { "CheckItem" };
679
680    void SetAvailableSpace(Component component, char * parentPath)
681    {
682       char path[MAX_LOCATION];
683       int c;
684       FileSize64 size = 0;
685
686       strcpy(path, parentPath);
687       PathCat(path, component.installPath);
688
689       if(component.subComponents)
690          for(c = 0; component.subComponents[c].name; c++)
691             SetAvailableSpace(component.subComponents[c], path);
692
693       while(!FileExists(path) && path[0])
694          StripLastDirectory(path, path);
695
696       if(path[0])
697          GetFreeSpace(path, &size);
698       component.row.SetData(avField, size);
699       if(!size) install.disabled = true;
700    }
701
702    FileSize ComputeSize(char * path)
703    {
704       FileSize size = 0;
705       FileAttribs attribs = FileExists(path);
706       if(attribs.isDirectory)
707       {
708          FileListing listing { path };
709          while(listing.Find())
710          {
711             if(listing.stats.attribs.isDirectory)
712                size += ComputeSize(listing.path);
713             else
714                size += listing.stats.size;
715          }
716       }
717       else
718          FileGetSize(path, &size);
719       return size;
720    }
721
722    void AddComponent(Component component, Component parent, char * parentPath)
723    {
724       DataRow row = (parent != null) ? parent.row.AddRow() : componentsBox.AddRow();
725       FileSize size = 0;
726       FileSize64 avSize = 0;
727       char path[MAX_LOCATION];
728
729       component.row = row;
730       strcpy(path, parentPath);
731       if(component.defInstallPath)
732          PathCat(path, component.defInstallPath);
733       component.parent = parent;
734          
735       row.SetData(null, CheckItem { component.name, component } );
736
737       if(component.defInstallPath)
738       {
739          strcpy(component.installPath, component.defInstallPath);
740          ChangeCh(component.installPath, '/', DIR_SEP);
741          row.SetData(locationField, component.installPath);
742       }
743
744       if(component.mandatory)
745          componentsBox.SetDisabled(row, true);
746       componentsBox.SetCheck(row, component.selected);
747
748       if(component.dataPath)
749       {
750          char path[MAX_LOCATION];
751          strcpy(path, ":");
752          PathCat(path, component.dataPath);
753          component.size = ComputeSize(path);
754       }
755       if(component.subComponents)
756       {
757          int c;
758          for(c = 0; component.subComponents[c].name; c++)
759          {
760             AddComponent(component.subComponents[c], component, path);
761             size += component.subComponents[c].requiredSize;
762          }
763       }
764
765       component.requiredSize = component.selected ? (size + component.size) : 0;
766       if(component.requiredSize)
767          row.SetData(reqField, component.requiredSize);
768
769       while(!FileExists(path) && path[0])
770          StripLastDirectory(path, path);
771       
772       if(path[0])
773          GetFreeSpace(path, &avSize);
774       else
775          avSize = 0;
776       row.SetData(avField, avSize);
777       row.collapsed = true;
778    }
779
780    void AddOption(InstallOption option, InstallOption parent)
781    {
782       DataRow row = option.row = (parent != null) ? parent.row.AddRow() : optionsBox.AddRow();
783       row.SetData(null, CheckItem { option.name, option } );
784       optionsBox.SetCheck(row, option.selected);
785       if(option.subOptions)
786       {
787          int c;
788          for(c = 0; option.subOptions[c].name; c++)
789          {
790             AddOption(option.subOptions[c], option);
791          }
792       }
793       row.collapsed = true;
794    }
795
796    Installer()
797    {
798       int c;
799       char programFilesDir[MAX_LOCATION];
800       char appData[MAX_LOCATION];
801       char homeDrive[MAX_LOCATION];
802       char winDir[MAX_LOCATION];
803
804       GetEnvironment("APPDATA", appData, sizeof(appData));
805       GetEnvironment("HOMEDRIVE", homeDrive, sizeof(homeDrive));
806       GetEnvironment("windir", winDir, sizeof(winDir));
807       
808       componentsBox.AddField(componentField);
809       componentsBox.AddField(locationField);
810       componentsBox.AddField(reqField);
811       componentsBox.AddField(avField);
812
813       optionsBox.AddField(optionField);
814
815       if(GetEnvironment("ProgramFiles", programFilesDir, MAX_LOCATION))
816       {
817          strcpy(installDir, programFilesDir);
818          PathCat(installDir, "ECERE SDK");
819       }
820       else if(homeDrive && homeDrive[0])
821       {
822          strcpy(installDir, homeDrive);
823          PathCat(installDir, "ECERE SDK");
824       }
825       else if(winDir && winDir[0])
826       {
827          strcpy(installDir, winDir);
828          PathCat(installDir, "..\\ECERE SDK");
829       }
830       else
831          strcpy(installDir, "C:\\ECERE SDK");
832       
833       if(appData && appData[0])
834       {
835          static char defSamplesPath[MAX_LOCATION];
836          static char defExtrasPath[MAX_LOCATION];
837          strcpy(defSamplesPath, appData);
838          PathCat(defSamplesPath, "ECERE SDK\\Samples");
839          components[samples].defInstallPath = defSamplesPath;
840
841          strcpy(defExtrasPath, appData);
842          PathCat(defExtrasPath, "ECERE SDK\\extras");
843          coreSDK[extras].defInstallPath = defExtrasPath;
844       }
845          
846       destBox.path = installDir;
847
848       {
849          ComponentID c;
850          for(c = 0; components[c].name; c++)
851             AddComponent(components[c], null, installDir);
852       }
853
854       // Compute size apart because ToggleCheck will change it
855       totalSize = 0;
856       {
857          ComponentID c;
858          for(c = 0; components[c].name; c++)
859             totalSize += components[c].requiredSize;
860       }
861       {
862          char sizeString[100];
863          PrintSize(sizeString, totalSize, 2);
864          totalSpaceValue.text = sizeString;
865       }
866       for(c = 0; options[c].name; c++)
867          AddOption(options[c], null);
868    }
869
870    bool OnCreate()
871    {
872       destBox.Activate();
873       return true;
874    }
875
876    void OnDrawOverChildren(Surface surface)
877    {
878       int tw = label2.size.w;
879       surface.SetForeground(Color { 128, 128, 128 });
880       surface.HLine(label2.position.x + tw + 6, 620, 400);
881       surface.SetForeground(white);
882       surface.HLine(label2.position.x + tw + 6, 621, 401);
883       surface.PutPixel(621, 400);
884    }
885
886    Label label3
887    {
888       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
889    };
890 }
891
892 class InstallProgress : Window
893 {
894    text = $"Ecere Software Development Kit Setup - v0.44 \"Ryōan-ji\"";
895    background = activeBorder;
896    borderStyle = fixed;
897    hasMinimize = true;
898    hasClose = true;
899    tabCycle = true;
900    // size = Size { 640, 480 };
901    clientSize = { 636, 456 };
902    //clientSize = { 796, 576 };
903    icon = { ":icon.png" };
904
905    Picture back { image = BitmapResource { ":ryoanji-progress.png" }, parent = this, position = { 0, 0 } };
906    Label installing { this, position = { 32, 160 } };
907    ProgressBar progressBar { parent = this, size = Size { 588, 24 }, anchor = Anchor { left = 24, top = 184 } };
908    Button finish
909    {
910       parent = this, text = $"Install", disabled = true, isDefault = true, size = Size { 75, 23 }, anchor = Anchor { left = 432, top = 416 };
911
912       NotifyClicked = ButtonCloseDialog
913    };
914    Button cancel
915    {
916       this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 };
917
918       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
919       {
920          abortInstall = true;
921          return true;
922       }
923    };
924    EditBox titleInfo
925    {
926       inactive = true, noSelect = true,
927       multiLine = true, parent = label3, opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
928       contents = $"Please wait while the Ecere Software Development Kit is being installed."
929    };
930    Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
931    Picture picture1
932    {
933       image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, anchor = Anchor { left = 16, top = 4 };
934       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
935
936       bool OnLeftButtonDown(int x, int y, Modifiers mods)
937       {
938          ShellOpen("http://www.ecere.com/");
939          return true;
940       }
941    };
942    Label title { parent = label3, text = $"Installing the ECERE SDK", font = FontResource { "Tahoma", 8.25f, bold = true }, size = Size { 326, 16 }, anchor = Anchor { horz = 91, vert = -12 } };
943
944    void OnDrawOverChildren(Surface surface)
945    {
946       int tw = label2.size.w;
947       surface.SetForeground(Color { 128, 128, 128 });
948       surface.HLine(label2.position.x + tw + 6, 620, 400);
949       surface.SetForeground(white);
950       surface.HLine(label2.position.x + tw + 6, 621, 401);
951       surface.PutPixel(621, 400);
952    }
953
954    Label label3
955    {
956       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
957    };
958    InstallThread thread
959    {
960    };
961 }
962
963 Installer installer {};
964 InstallProgress installProgress { autoCreate = false };
965
966 void ModifyPath(char * newPath)
967 {
968    char * paths[200];
969    int p, count;
970    char oldPath[8192];
971    CoreSDKID c;
972
973    strcpy(oldPath, newPath);
974    count = TokenizeWith(oldPath, sizeof(paths) / sizeof(char *), paths, ";", false);
975
976    for(c = 0; coreSDK[c].name; c++)
977    {
978       char path[MAX_LOCATION];
979       if(c == extras || c == vanilla) continue;
980
981       coreSDK[c].GetFullPath(path);
982       if(c != ide && c != runtime && c != eda && c != ec)
983       {
984 #ifndef NOMINGW
985          if(!pathOptions[PathOptions::AddMinGWPaths].selected)
986 #endif
987          continue;
988          PathCat(path, "bin");
989       }
990       else if(!pathOptions[PathOptions::AddECEREPaths].selected) continue;
991
992       for(p = 0; p<count; p++)
993          if(!fstrcmp(paths[p], path))
994             break;
995       if(p == count)
996       {
997          char * start;
998          if(count) 
999          {
1000             strcat(newPath, ";");
1001             start = paths[count-1] + strlen(paths[count-1])+1;
1002          }
1003          else
1004             start = oldPath;
1005          
1006          strcpy(start, path);
1007          *(start + strlen(path)) = '\0';
1008          paths[count++] = start;
1009
1010          strcat(newPath, path);
1011       }
1012    }
1013 #ifndef NOMINGW
1014    if(pathOptions[PathOptions::AddMinGWPaths].selected)
1015    {
1016       int c;
1017       // Up to before Win32 APIs
1018       for(c = 0; c < 4 /*additional[c].name*/; c++)
1019       {
1020          char path[MAX_LOCATION];
1021          additional[c].GetFullPath(path);
1022          if(c != 0) // UPX already is in bin
1023             PathCat(path, "bin");
1024          for(p = 0; p<count; p++)
1025             if(!fstrcmp(paths[p], path))
1026                break;
1027
1028          if(p == count)
1029          {
1030             char * start;
1031             if(count) 
1032             {
1033                strcat(newPath, ";");
1034                start = paths[count-1] + strlen(paths[count-1])+1;
1035             }
1036             else
1037                start = oldPath;
1038             
1039             strcpy(start, path);
1040             *(start + strlen(path)) = '\0';
1041             paths[count++] = start;
1042
1043             strcat(newPath, path);
1044          }
1045       }
1046    }
1047 #endif
1048 }
1049
1050 void AssociateExtension(char * extension, char * description, char *name, char * action, char * path)
1051 {
1052    HKEY key;
1053    uint status, size;
1054    char keyName[1024];
1055
1056    RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1057    RegSetValueEx(key, null, 0, REG_SZ, name, (uint)strlen(name)+1);
1058    RegCloseKey(key);
1059
1060    RegCreateKeyEx(HKEY_CLASSES_ROOT, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1061    RegSetValueEx(key, null, 0, REG_SZ, description, (uint)strlen(description)+1);
1062    RegCloseKey(key);
1063
1064    sprintf(keyName, "%s\\shell", extension);
1065    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1066    RegSetValueEx(key, null, 0, REG_SZ, action, (uint)strlen(action)+1);
1067    RegCloseKey(key);
1068
1069    sprintf(keyName, "%s\\shell\\%s", name, action);
1070    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1071    RegCloseKey(key);
1072
1073    sprintf(keyName, "%s\\shell\\%s\\command", name, action);
1074    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1075    
1076    sprintf(keyName, path);
1077    strcat(keyName, " \"%L\"");
1078    {
1079       uint16 wKeyName[2048];
1080       UTF8toUTF16Buffer(keyName, wKeyName, sizeof(wKeyName) / sizeof(uint16));
1081       RegSetValueExW(key, null, 0, REG_SZ, (byte *)wKeyName, (uint)(wcslen(wKeyName) + 1)*sizeof(uint16));
1082    }
1083    RegCloseKey(key);
1084 }
1085
1086 class InstallThread : Thread
1087 {
1088    unsigned int Main()
1089    {
1090       ComponentID c;
1091       ((GuiApplication)__thisModule).Lock();
1092       installProgress.progressBar.range = totalSize;
1093       for(c = 0; components[c].name && !abortInstall; c++)
1094          components[c].Install(installDir);
1095       if(abortInstall)
1096       {
1097          installProgress.progressBar.range = 0;
1098          installProgress.finish.Destroy(0);
1099          installProgress.cancel.text = $"Close";
1100          installProgress.cancel.isDefault = true;
1101          installProgress.cancel.disabled = false;
1102          installProgress.cancel.NotifyClicked = Window::ButtonCloseDialog;
1103          installProgress.installing.text = $"Installation Cancelled.";
1104          installProgress.title.text = $"Installation Cancelled";
1105          installProgress.titleInfo.contents = $"The installation was not completed.";
1106       }
1107       else
1108       {
1109          CoreSDKID c;
1110          char idePath[MAX_LOCATION];
1111          char userProfile[MAX_LOCATION];
1112
1113          // Configure IDE
1114          IDESettings settings = null; // Don't instantiate yet so we can pick up old settings
1115
1116          IDESettingsContainer settingsContainer
1117          {
1118             driver = "JSON";
1119             dataOwner = &settings;
1120             dataClass = class(IDESettings);
1121          };
1122          CompilerConfig compiler;
1123          installProgress.installing.text = $"Configuring ECERE IDE...";
1124          ((GuiApplication)__thisModule).Unlock();
1125          ((GuiApplication)__thisModule).SignalEvent();
1126
1127          settingsContainer.Load();
1128          compiler = settings.GetCompilerConfig(defaultCompilerName);
1129          if(compiler)
1130          {
1131             for(c = 0; coreSDK[c].name; c++)
1132             {
1133                char path[MAX_LOCATION];
1134
1135                if(c == extras) continue;
1136                coreSDK[c].GetFullPath(path);
1137                if(c != ide && c != runtime && c != eda && c != ec && c != vanilla)
1138                   PathCat(path, "bin");
1139                MakeSlashPath(path);
1140                if(c == ide)
1141                {
1142                   coreSDK[c].GetFullPath(idePath);
1143                   PathCat(idePath, "IDE.exe");
1144                }
1145
1146                if(c != vanilla)
1147                {
1148                   if(!compiler.executableDirs.Find(path))
1149                      compiler.executableDirs.Add(CopyString(path));
1150                }
1151
1152                if(c == runtime || c == vanilla)
1153                {
1154                   if(!compiler.libraryDirs.Find(path))
1155                      compiler.libraryDirs.Add(CopyString(path));
1156                }
1157             }
1158    #ifndef NOMINGW
1159             // Up to before Win32 APIs
1160             for(c = 0; c < 4 /*additional[c].name*/; c++)
1161             {
1162                char path[MAX_LOCATION];
1163                additional[c].GetFullPath(path);
1164                if(c != 0) // upx
1165                   PathCat(path, "bin");
1166                MakeSlashPath(path);
1167                if(!compiler.executableDirs.Find(path))
1168                   compiler.executableDirs.Add(CopyString(path));
1169             }
1170    #endif
1171          }
1172
1173          {
1174             char path[MAX_LOCATION] = "";
1175
1176             if(components[samples].selected)
1177                components[samples].GetFullPath(path);
1178             else
1179                components[coreSDK].GetFullPath(path);
1180
1181             if(!settings.ideProjectFileDialogLocation[0])
1182                settings.ideProjectFileDialogLocation = path;
1183             if(!settings.ideFileDialogLocation[0])
1184                settings.ideFileDialogLocation = path;
1185
1186             if(documentation[apiRef].selected)
1187             {
1188                documentation[apiRef].GetFullPath(path);
1189                if(!settings.docDir[0])
1190                   settings.docDir = path;
1191             }
1192          }
1193
1194          settingsContainer.Save();
1195          delete settingsContainer;
1196          delete settings;
1197
1198          // Set up Uninstaller
1199          ((GuiApplication)__thisModule).Lock();
1200          installProgress.installing.text = $"Registering uninstaller...";
1201          ((GuiApplication)__thisModule).Unlock();
1202          ((GuiApplication)__thisModule).SignalEvent();
1203
1204          {
1205             HKEY key;
1206             uint status, size;
1207             char * displayName = "ECERE SDK 0.44";
1208             char uninstaller[MAX_LOCATION];
1209             bool nomodify = true;
1210
1211             strcpy(uninstaller, installDir);
1212             PathCat(uninstaller, "uninstall_ecere.exe");
1213
1214             RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ECERE SDK", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1215
1216             RegSetValueEx(key, "DisplayName", 0, REG_SZ, displayName, (uint)strlen(displayName)+1);
1217             RegSetValueEx(key, "UninstallString", 0, REG_SZ, uninstaller, (uint)strlen(uninstaller)+1);
1218             RegSetValueEx(key, "DisplayIcon", 0, REG_SZ, idePath, (uint)strlen(idePath)+1);
1219             //RegSetValueEx(key, "NoModify", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1220             //RegSetValueEx(key, "NoRepair", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1221             RegCloseKey(key);
1222          }
1223
1224          // Add paths
1225          if(pathOptions[PathOptions::AddECEREPaths].selected 
1226 #ifndef NOMINGW
1227             || pathOptions[PathOptions::AddMinGWPaths].selected
1228 #endif
1229             )
1230          {
1231             HKEY key;
1232             uint status, size;
1233             char path[4096] = "";
1234             uint16 wPath[4096];
1235
1236             ((GuiApplication)__thisModule).Lock();
1237             installProgress.installing.text = "Registering paths...";
1238             ((GuiApplication)__thisModule).Unlock();
1239             ((GuiApplication)__thisModule).SignalEvent();
1240                         
1241             // if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Environment", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1242             
1243             RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1244             // RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1245             if(status == REG_OPENED_EXISTING_KEY)
1246             {
1247                size = sizeof(wPath);
1248                RegQueryValueExW(key, L"path", null, null, (byte *)wPath, &size);
1249                UTF16toUTF8Buffer(wPath, path, sizeof(path));
1250             }
1251             ModifyPath(path);
1252             UTF8toUTF16Buffer(path, wPath, sizeof(wPath) / sizeof(uint16));
1253             RegSetValueExW(key, L"path", 0, REG_EXPAND_SZ, (byte *)wPath, (uint)(wcslen(wPath)+1) * 2);
1254             RegCloseKey(key);
1255
1256             SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, 0, (int)"Environment", SMTO_NORMAL, 1000, NULL);
1257          }
1258
1259          // Install Program Group Icons
1260          GetEnvironment("USERPROFILE", userProfile, sizeof(userProfile));
1261
1262          if(options[IconOptions::StartMenuIcon].selected)
1263          {
1264             char destPath[MAX_LOCATION];
1265             char startMenuPath[MAX_LOCATION] = "";
1266             HKEY key;
1267
1268             ((GuiApplication)__thisModule).Lock();
1269             installProgress.installing.text = $"Installing Start Menu Icons...";
1270             ((GuiApplication)__thisModule).Unlock();
1271             ((GuiApplication)__thisModule).SignalEvent();
1272
1273             strcpy(destPath, userProfile);
1274
1275             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1276             {
1277                uint16 wStartMenuPath[2048];
1278                uint size = sizeof(wStartMenuPath);
1279                // RegQueryValueEx(key, "Start Menu", null, null, startMenuPath, &size);
1280                RegQueryValueExW(key, L"Programs", null, null, (byte *)wStartMenuPath, &size);
1281                UTF16toUTF8Buffer(wStartMenuPath, startMenuPath, sizeof(startMenuPath));
1282                RegCloseKey(key);
1283             }
1284             if(!startMenuPath[0] && userProfile[0])
1285             {
1286                strcpy(startMenuPath, userProfile);
1287                PathCat(startMenuPath, "Start Menu\\Programs");
1288             }
1289
1290             if(startMenuPath[0])
1291             {
1292                strcpy(destPath, startMenuPath);
1293                PathCat(destPath, "ECERE SDK");
1294                MakeDir(destPath);
1295
1296                strcpy(destPath, startMenuPath);
1297                PathCat(destPath, "ECERE SDK\\ECERE IDE.lnk");
1298                CreateLink(idePath, destPath, null); //"ECERE IDE");
1299                if(components[samples].selected)
1300                {
1301                   char samplesPath[MAX_LOCATION] = "";
1302                   components[samples].GetFullPath(samplesPath);
1303
1304                   strcpy(destPath, startMenuPath);
1305                   PathCat(destPath, "ECERE SDK\\Sample Projects.lnk");
1306                   CreateLink(samplesPath, destPath, null);//"Sample Projects");
1307                }
1308                if(components[documentation].selected && documentation[ecereBook].selected)
1309                {
1310                   char docPath[MAX_LOCATION] = "";
1311                   documentation[ecereBook].GetFullPath(docPath);
1312                   PathCat(docPath, "Ecere Tao of Programming [work in progress].pdf");
1313
1314                   {
1315                      char tao[MAX_LOCATION] ;
1316                      documentation[ecereBook].GetFullPath(tao);
1317                      PathCat(tao, "tao.pdf");
1318                      RenameFile(tao, docPath);
1319                   }
1320
1321                   strcpy(destPath, startMenuPath);
1322                   PathCat(destPath, "ECERE SDK\\The Ecere Tao of Programming.lnk");
1323                   CreateLink(docPath, destPath, null);
1324                }
1325             }
1326          }
1327
1328          // Install Desktop Icon
1329          if(options[IconOptions::DesktopIcon].selected)
1330          {
1331             HKEY key;
1332             char desktopPath[MAX_LOCATION];
1333
1334             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1335             {
1336                uint16 wDesktopPath[MAX_LOCATION];
1337                uint size = sizeof(wDesktopPath);
1338                RegQueryValueExW(key, L"Desktop", null, null, (byte *)wDesktopPath, &size);
1339                UTF16toUTF8Buffer(wDesktopPath, desktopPath, sizeof(desktopPath));
1340                RegCloseKey(key);
1341             }
1342             if(!desktopPath[0] && userProfile[0])
1343             {
1344                strcpy(desktopPath, userProfile);
1345                PathCat(desktopPath, "Desktop");
1346             }
1347
1348             if(desktopPath[0])
1349             {
1350                PathCat(desktopPath, "ECERE IDE.lnk");
1351
1352                ((GuiApplication)__thisModule).Lock();
1353                installProgress.installing.text = $"Installing Desktop Icon...";
1354                ((GuiApplication)__thisModule).Unlock();
1355                ((GuiApplication)__thisModule).SignalEvent();
1356
1357                CreateLink(idePath, desktopPath, null);//"ECERE IDE");
1358             }
1359          }
1360
1361          // Install QuickLaunch Icon
1362          if(options[IconOptions::QuickLaunchIcon].selected)
1363          {
1364             char appData[MAX_LOCATION];
1365             GetEnvironment("APPDATA", appData, sizeof(appData));
1366
1367             if(appData[0])
1368             {
1369                char destPath[MAX_LOCATION];
1370
1371                ((GuiApplication)__thisModule).Lock();
1372                installProgress.installing.text = $"Installing Quicklaunch Icon...";
1373                ((GuiApplication)__thisModule).Unlock();
1374                ((GuiApplication)__thisModule).SignalEvent();
1375
1376                strcpy(destPath, appData);
1377                PathCat(destPath, "Microsoft\\Internet Explorer\\Quick Launch\\ECERE IDE.lnk");
1378
1379                CreateLink(idePath, destPath, null);//"ECERE IDE");
1380             }
1381          }
1382
1383          // Register File Types
1384          if(associateOptions[AssociateOptions::AssociateEPJ].selected ||
1385             associateOptions[AssociateOptions::AssociateEC].selected ||
1386             associateOptions[AssociateOptions::AssociateC].selected ||
1387             associateOptions[AssociateOptions::AssociateCPP].selected ||
1388             associateOptions[AssociateOptions::AssociateTXT].selected ||
1389             associateOptions[AssociateOptions::Associate3DS].selected ||
1390             associateOptions[AssociateOptions::AssociateIMG].selected)
1391          {
1392             ((GuiApplication)__thisModule).Lock();
1393             installProgress.installing.text = $"Resgistering File Types...";
1394             ((GuiApplication)__thisModule).Unlock();
1395             ((GuiApplication)__thisModule).SignalEvent();
1396             
1397             if(associateOptions[AssociateOptions::AssociateEPJ].selected)
1398             {
1399                AssociateExtension(".epj", "ECERE IDE Project", "epj_file", "Open", idePath);
1400             }
1401             if(associateOptions[AssociateOptions::AssociateEC].selected)
1402             {
1403                AssociateExtension(".ec", "eC Source File", "ec_file", "Open", idePath);
1404                AssociateExtension(".eh", "eC Header File", "eh_file", "Open", idePath);
1405             }
1406             if(associateOptions[AssociateOptions::AssociateC].selected)
1407             {
1408                AssociateExtension(".c", "C Source File", "c_file", "Open", idePath);
1409                AssociateExtension(".h", "C Header File", "h_file", "Open", idePath);
1410             }
1411             if(associateOptions[AssociateOptions::AssociateCPP].selected)
1412             {
1413                AssociateExtension(".cpp", "C++ Source File", "cpp_file", "Open", idePath);
1414                AssociateExtension(".cc", "C++ Source File", "cpp_file", "Open", idePath);
1415                AssociateExtension(".cxx", "C++ Source File", "cpp_file", "Open", idePath);
1416                AssociateExtension(".chh", "C++ Header File", "chh_file", "Open", idePath);
1417                AssociateExtension(".hh", "C++ Header File", "chh_file", "Open", idePath);
1418                AssociateExtension(".hxx", "C++ Header File", "chh_file", "Open", idePath);
1419             }
1420             if(associateOptions[AssociateOptions::AssociateTXT].selected)
1421             {
1422                AssociateExtension(".txt", "Text File", "txt_file", "Open", idePath);
1423             }
1424             if(associateOptions[AssociateOptions::Associate3DS].selected)
1425             {
1426                AssociateExtension(".3ds", "3D Studio Model", "3ds_file", "View", idePath);
1427             }
1428             if(associateOptions[AssociateOptions::AssociateIMG].selected)
1429             {
1430                AssociateExtension(".bmp", "BMP Image", "bmp_file", "View", idePath);
1431                AssociateExtension(".pcx", "PCX Image", "pcx_file", "View", idePath);
1432                AssociateExtension(".png", "PNG Image", "png_file", "View", idePath);
1433                AssociateExtension(".jpg", "JPEG Image", "jpg_file", "View", idePath);
1434                AssociateExtension(".jpeg", "JPEG Image", "jpg_file", "View", idePath);
1435                AssociateExtension(".gif", "GIF Image", "gif_file", "View", idePath);
1436             }
1437          }
1438          ((GuiApplication)__thisModule).Lock();
1439
1440          installProgress.cancel.Destroy(0);
1441          installProgress.finish.text = $"Finish";
1442          installProgress.finish.disabled = false;
1443          installProgress.finish.Activate();
1444          installProgress.installing.text = $"Installation Complete.";
1445          installProgress.title.text = $"Installation Complete";
1446          installProgress.titleInfo.contents = $"Thank you for using the Ecere SDK.";
1447       }
1448       ((GuiApplication)__thisModule).Unlock();
1449       return 0;
1450    }
1451 }