installer: Using external checkbox; various fixes and improvements
[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       bool NotifyChanged(CheckListBox listBox, DataRow row)
559       {
560          CheckItem * item = row.GetData(optionField);
561          InstallOption * option = item->data;
562          int c;
563          option->selected = listBox.IsChecked(row);
564          return true;
565       }
566    };
567    Button install
568    {
569       parent = this, text = $"Install", isDefault = true, size = { 75, 23 }, position = { 432, 416 };
570
571       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
572       {
573          installProgress.Create();
574          Destroy(0);
575          // installProgress.thread.Main();
576          installProgress.thread.Create();
577          return true;
578       }
579    };
580    Button button3 { parent = this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 }, NotifyClicked = ButtonCloseDialog };
581    Label label1 { labeledWindow = destBox, tabCycle = true, isGroupBox = true, parent = this, inactive = false, size = Size { 458, 50 }, anchor = Anchor { left = 160, top = 96 } };
582    PathBox destBox
583    {
584       parent = label1, master = this, text = $" Destination Folder", size = Size { 336, 22 }, anchor = Anchor { left = 12, top = 20, right = 12 };
585       typeExpected = directory;
586       browseDialog = fileDialog;
587       opacity = 0;
588
589       bool NotifyModified(PathBox pathBox)
590       {
591          ComponentID c;
592          strcpy(installDir, destBox.path);
593          install.disabled = false;
594          for(c = 0; components[c].name; c++)
595          {
596             SetAvailableSpace(components[c], installDir);
597          }
598          return true;
599       }
600    };
601    /*Button button1
602    {
603       label1, this, $"Browse", altB, size = { 83, 24 }, position = { 360, 16 };
604
605       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
606       {
607          strcpy(fileDialog.filePath, installDir);
608          StripLastDirectory(installDir, fileDialog.currentDirectory);
609          if(fileDialog.Modal())
610          {
611             strcpy(installDir, fileDialog.filePath);
612             destBox.contents = installDir;
613             // TOCHECK: Should setting contents call NotifyModified?
614             destBox.NotifyModified(destBox.master, destBox);
615          }
616          return true;
617       }
618    };*/
619    EditBox label5
620    {
621       this, multiLine = true,
622       opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 96 }, noSelect = true,
623       contents = $"Select the default root\n"
624          "folder where to install\n"
625          "all components:"
626    };
627    EditBox label6
628    {
629       this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 132 }, position = { 14, 152 }, noSelect = true,
630       multiLine = true,
631       contents = $"Select the optional\n"
632          "components you wish\n"
633          "to install:\n\n"
634          "You may customize the\n"
635          "install location for each\n"
636          "of them, or use the default\n"
637          "based on the main\n"
638          "destination folder."
639    };
640    EditBox label7
641    {
642       this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 280 }, noSelect = true, 
643       multiLine = true,
644       contents = $"Select icons to install, file\n"
645       "associations, and system\n"
646       "environment modifications:"
647    };
648    Label totalSpaceLabel
649    {
650       this, position = { 18, 352 }, text = $"Space Required: "
651    };
652    Label totalSpaceValue
653    {
654       this, position = { 100, 352 }, text = "0 mb"
655    };
656    EditBox editBox1
657    {
658       inactive = true, noSelect = true,
659       multiLine = true, parent = label3, text = "editBox1", opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
660       contents = $"Choose in which folder to install the ECERE SDK, which features\n"
661          "of the SDK to install, as well as where to install program icons."
662    };
663    Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
664    Picture picture1
665    {
666       image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, text = "picture1", anchor = Anchor { left = 16, top = 4 };
667       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
668
669       bool OnLeftButtonDown(int x, int y, Modifiers mods)
670       {
671          ShellOpen("http://www.ecere.com/");
672          return true;
673       }
674    };
675    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 } };
676    DataField componentField { "CheckItem", width = 140, header = $"Component" };
677    DataField locationField { "char *", width = 108, header = $"Destination Folder", editable = true };
678    DataField reqField { "FileSize", width = 80, header = $"Req. Space", alignment = right };
679    DataField avField { "FileSize64", width = 80, header = $"Avail. Space", alignment = right };
680    DataField optionField { "CheckItem" };
681
682    void SetAvailableSpace(Component component, char * parentPath)
683    {
684       char path[MAX_LOCATION];
685       int c;
686       FileSize64 size = 0;
687
688       strcpy(path, parentPath);
689       PathCat(path, component.installPath);
690
691       if(component.subComponents)
692          for(c = 0; component.subComponents[c].name; c++)
693             SetAvailableSpace(component.subComponents[c], path);
694
695       while(!FileExists(path) && path[0])
696          StripLastDirectory(path, path);
697
698       if(path[0])
699          GetFreeSpace(path, &size);
700       component.row.SetData(avField, size);
701       if(!size) install.disabled = true;
702    }
703
704    FileSize ComputeSize(char * path)
705    {
706       FileSize size = 0;
707       FileAttribs attribs = FileExists(path);
708       if(attribs.isDirectory)
709       {
710          FileListing listing { path };
711          while(listing.Find())
712          {
713             if(listing.stats.attribs.isDirectory)
714                size += ComputeSize(listing.path);
715             else
716                size += listing.stats.size;
717          }
718       }
719       else
720          FileGetSize(path, &size);
721       return size;
722    }
723
724    void AddComponent(Component component, Component parent, char * parentPath)
725    {
726       DataRow row = (parent != null) ? parent.row.AddRow() : componentsBox.AddRow();
727       FileSize size = 0;
728       FileSize64 avSize = 0;
729       char path[MAX_LOCATION];
730
731       component.row = row;
732       strcpy(path, parentPath);
733       if(component.defInstallPath)
734          PathCat(path, component.defInstallPath);
735       component.parent = parent;
736          
737       row.SetData(null, CheckItem { component.name, component } );
738
739       if(component.defInstallPath)
740       {
741          strcpy(component.installPath, component.defInstallPath);
742          ChangeCh(component.installPath, '/', DIR_SEP);
743          row.SetData(locationField, component.installPath);
744       }
745
746       if(component.mandatory)
747          componentsBox.SetDisabled(row, true);
748       componentsBox.SetCheck(row, component.selected);
749
750       if(component.dataPath)
751       {
752          char path[MAX_LOCATION];
753          strcpy(path, ":");
754          PathCat(path, component.dataPath);
755          component.size = ComputeSize(path);
756       }
757       if(component.subComponents)
758       {
759          int c;
760          for(c = 0; component.subComponents[c].name; c++)
761          {
762             AddComponent(component.subComponents[c], component, path);
763             size += component.subComponents[c].requiredSize;
764          }
765       }
766
767       component.requiredSize = component.selected ? (size + component.size) : 0;
768       if(component.requiredSize)
769          row.SetData(reqField, component.requiredSize);
770
771       while(!FileExists(path) && path[0])
772          StripLastDirectory(path, path);
773       
774       if(path[0])
775          GetFreeSpace(path, &avSize);
776       else
777          avSize = 0;
778       row.SetData(avField, avSize);
779       row.collapsed = true;
780    }
781
782    void AddOption(InstallOption option, InstallOption parent)
783    {
784       DataRow row = option.row = (parent != null) ? parent.row.AddRow() : optionsBox.AddRow();
785       row.SetData(null, CheckItem { option.name, option } );
786       optionsBox.SetCheck(row, option.selected);
787       if(option.subOptions)
788       {
789          int c;
790          for(c = 0; option.subOptions[c].name; c++)
791          {
792             AddOption(option.subOptions[c], option);
793          }
794       }
795       row.collapsed = true;
796    }
797
798    Installer()
799    {
800       int c;
801       char programFilesDir[MAX_LOCATION];
802       char appData[MAX_LOCATION];
803       char homeDrive[MAX_LOCATION];
804       char winDir[MAX_LOCATION];
805
806       GetEnvironment("APPDATA", appData, sizeof(appData));
807       GetEnvironment("HOMEDRIVE", homeDrive, sizeof(homeDrive));
808       GetEnvironment("windir", winDir, sizeof(winDir));
809       
810       componentsBox.AddField(componentField);
811       componentsBox.AddField(locationField);
812       componentsBox.AddField(reqField);
813       componentsBox.AddField(avField);
814
815       optionsBox.AddField(optionField);
816
817       if(GetEnvironment("ProgramFiles", programFilesDir, MAX_LOCATION))
818       {
819          strcpy(installDir, programFilesDir);
820          PathCat(installDir, "ECERE SDK");
821       }
822       else if(homeDrive && homeDrive[0])
823       {
824          strcpy(installDir, homeDrive);
825          PathCat(installDir, "ECERE SDK");
826       }
827       else if(winDir && winDir[0])
828       {
829          strcpy(installDir, winDir);
830          PathCat(installDir, "..\\ECERE SDK");
831       }
832       else
833          strcpy(installDir, "C:\\ECERE SDK");
834       
835       if(appData && appData[0])
836       {
837          static char defSamplesPath[MAX_LOCATION];
838          static char defExtrasPath[MAX_LOCATION];
839          strcpy(defSamplesPath, appData);
840          PathCat(defSamplesPath, "ECERE SDK\\Samples");
841          components[samples].defInstallPath = defSamplesPath;
842
843          strcpy(defExtrasPath, appData);
844          PathCat(defExtrasPath, "ECERE SDK\\extras");
845          coreSDK[extras].defInstallPath = defExtrasPath;
846       }
847          
848       destBox.path = installDir;
849
850       {
851          ComponentID c;
852          for(c = 0; components[c].name; c++)
853             AddComponent(components[c], null, installDir);
854       }
855
856       // Compute size apart because ToggleCheck will change it
857       totalSize = 0;
858       {
859          ComponentID c;
860          for(c = 0; components[c].name; c++)
861             totalSize += components[c].requiredSize;
862       }
863       {
864          char sizeString[100];
865          PrintSize(sizeString, totalSize, 2);
866          totalSpaceValue.text = sizeString;
867       }
868       for(c = 0; options[c].name; c++)
869          AddOption(options[c], null);
870    }
871
872    bool OnCreate()
873    {
874       destBox.Activate();
875       return true;
876    }
877
878    void OnDrawOverChildren(Surface surface)
879    {
880       int tw = label2.size.w;
881       surface.SetForeground(Color { 128, 128, 128 });
882       surface.HLine(label2.position.x + tw + 6, 620, 400);
883       surface.SetForeground(white);
884       surface.HLine(label2.position.x + tw + 6, 621, 401);
885       surface.PutPixel(621, 400);
886    }
887
888    Label label3
889    {
890       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
891    };
892 }
893
894 class InstallProgress : Window
895 {
896    text = $"Ecere Software Development Kit Setup - v0.44 \"Ryōan-ji\"";
897    background = activeBorder;
898    borderStyle = fixed;
899    hasMinimize = true;
900    hasClose = true;
901    tabCycle = true;
902    // size = Size { 640, 480 };
903    clientSize = { 636, 456 };
904    //clientSize = { 796, 576 };
905    icon = { ":icon.png" };
906
907    Picture back { image = BitmapResource { ":ryoanji-progress.png" }, parent = this, position = { 0, 0 } };
908    Label installing { this, position = { 32, 160 } };
909    ProgressBar progressBar { parent = this, size = Size { 588, 24 }, anchor = Anchor { left = 24, top = 184 } };
910    Button finish
911    {
912       parent = this, text = $"Install", disabled = true, isDefault = true, size = Size { 75, 23 }, anchor = Anchor { left = 432, top = 416 };
913
914       NotifyClicked = ButtonCloseDialog
915    };
916    Button cancel
917    {
918       this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 };
919
920       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
921       {
922          abortInstall = true;
923          return true;
924       }
925    };
926    EditBox titleInfo
927    {
928       inactive = true, noSelect = true,
929       multiLine = true, parent = label3, opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
930       contents = $"Please wait while the Ecere Software Development Kit is being installed."
931    };
932    Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
933    Picture picture1
934    {
935       image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, anchor = Anchor { left = 16, top = 4 };
936       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
937
938       bool OnLeftButtonDown(int x, int y, Modifiers mods)
939       {
940          ShellOpen("http://www.ecere.com/");
941          return true;
942       }
943    };
944    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 } };
945
946    void OnDrawOverChildren(Surface surface)
947    {
948       int tw = label2.size.w;
949       surface.SetForeground(Color { 128, 128, 128 });
950       surface.HLine(label2.position.x + tw + 6, 620, 400);
951       surface.SetForeground(white);
952       surface.HLine(label2.position.x + tw + 6, 621, 401);
953       surface.PutPixel(621, 400);
954    }
955
956    Label label3
957    {
958       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
959    };
960    InstallThread thread
961    {
962    };
963 }
964
965 Installer installer {};
966 InstallProgress installProgress { autoCreate = false };
967
968 void ModifyPath(char * newPath)
969 {
970    char * paths[200];
971    int p, count;
972    char oldPath[8192];
973    CoreSDKID c;
974
975    strcpy(oldPath, newPath);
976    count = TokenizeWith(oldPath, sizeof(paths) / sizeof(char *), paths, ";", false);
977
978    for(c = 0; coreSDK[c].name; c++)
979    {
980       char path[MAX_LOCATION];
981       if(c == extras || c == vanilla) continue;
982
983       coreSDK[c].GetFullPath(path);
984       if(c != ide && c != runtime && c != eda && c != ec)
985       {
986 #ifndef NOMINGW
987          if(!pathOptions[PathOptions::AddMinGWPaths].selected)
988 #endif
989          continue;
990          PathCat(path, "bin");
991       }
992       else if(!pathOptions[PathOptions::AddECEREPaths].selected) continue;
993
994       for(p = 0; p<count; p++)
995          if(!fstrcmp(paths[p], path))
996             break;
997       if(p == count)
998       {
999          char * start;
1000          if(count) 
1001          {
1002             strcat(newPath, ";");
1003             start = paths[count-1] + strlen(paths[count-1])+1;
1004          }
1005          else
1006             start = oldPath;
1007          
1008          strcpy(start, path);
1009          *(start + strlen(path)) = '\0';
1010          paths[count++] = start;
1011
1012          strcat(newPath, path);
1013       }
1014    }
1015 #ifndef NOMINGW
1016    if(pathOptions[PathOptions::AddMinGWPaths].selected)
1017    {
1018       int c;
1019       // Up to before Win32 APIs
1020       for(c = 0; c < 4 /*additional[c].name*/; c++)
1021       {
1022          char path[MAX_LOCATION];
1023          additional[c].GetFullPath(path);
1024          if(c != 0) // UPX already is in bin
1025             PathCat(path, "bin");
1026          for(p = 0; p<count; p++)
1027             if(!fstrcmp(paths[p], path))
1028                break;
1029
1030          if(p == count)
1031          {
1032             char * start;
1033             if(count) 
1034             {
1035                strcat(newPath, ";");
1036                start = paths[count-1] + strlen(paths[count-1])+1;
1037             }
1038             else
1039                start = oldPath;
1040             
1041             strcpy(start, path);
1042             *(start + strlen(path)) = '\0';
1043             paths[count++] = start;
1044
1045             strcat(newPath, path);
1046          }
1047       }
1048    }
1049 #endif
1050 }
1051
1052 void AssociateExtension(char * extension, char * description, char *name, char * action, char * path)
1053 {
1054    HKEY key;
1055    uint status, size;
1056    char keyName[1024];
1057
1058    RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1059    RegSetValueEx(key, null, 0, REG_SZ, name, (uint)strlen(name)+1);
1060    RegCloseKey(key);
1061
1062    RegCreateKeyEx(HKEY_CLASSES_ROOT, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1063    RegSetValueEx(key, null, 0, REG_SZ, description, (uint)strlen(description)+1);
1064    RegCloseKey(key);
1065
1066    sprintf(keyName, "%s\\shell", extension);
1067    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1068    RegSetValueEx(key, null, 0, REG_SZ, action, (uint)strlen(action)+1);
1069    RegCloseKey(key);
1070
1071    sprintf(keyName, "%s\\shell\\%s", name, action);
1072    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1073    RegCloseKey(key);
1074
1075    sprintf(keyName, "%s\\shell\\%s\\command", name, action);
1076    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1077    
1078    sprintf(keyName, path);
1079    strcat(keyName, " \"%L\"");
1080    {
1081       uint16 wKeyName[2048];
1082       UTF8toUTF16Buffer(keyName, wKeyName, sizeof(wKeyName) / sizeof(uint16));
1083       RegSetValueExW(key, null, 0, REG_SZ, (byte *)wKeyName, (uint)(wcslen(wKeyName) + 1)*sizeof(uint16));
1084    }
1085    RegCloseKey(key);
1086 }
1087
1088 class InstallThread : Thread
1089 {
1090    unsigned int Main()
1091    {
1092       ComponentID c;
1093       ((GuiApplication)__thisModule).Lock();
1094       installProgress.progressBar.range = totalSize;
1095       for(c = 0; components[c].name && !abortInstall; c++)
1096          components[c].Install(installDir);
1097       if(abortInstall)
1098       {
1099          installProgress.progressBar.range = 0;
1100          installProgress.finish.Destroy(0);
1101          installProgress.cancel.text = $"Close";
1102          installProgress.cancel.isDefault = true;
1103          installProgress.cancel.disabled = false;
1104          installProgress.cancel.NotifyClicked = Window::ButtonCloseDialog;
1105          installProgress.installing.text = $"Installation Cancelled.";
1106          installProgress.title.text = $"Installation Cancelled";
1107          installProgress.titleInfo.contents = $"The installation was not completed.";
1108       }
1109       else
1110       {
1111          CoreSDKID c;
1112          char idePath[MAX_LOCATION];
1113          char userProfile[MAX_LOCATION];
1114
1115          // Configure IDE
1116          IDESettings settings = null; // Don't instantiate yet so we can pick up old settings
1117
1118          IDESettingsContainer settingsContainer
1119          {
1120             driver = "JSON";
1121             dataOwner = &settings;
1122             dataClass = class(IDESettings);
1123          };
1124          CompilerConfig compiler;
1125          installProgress.installing.text = $"Configuring ECERE IDE...";
1126          ((GuiApplication)__thisModule).Unlock();
1127          ((GuiApplication)__thisModule).SignalEvent();
1128
1129          settingsContainer.Load();
1130          compiler = settings.GetCompilerConfig(defaultCompilerName);
1131          if(compiler)
1132          {
1133             for(c = 0; coreSDK[c].name; c++)
1134             {
1135                char path[MAX_LOCATION];
1136
1137                if(c == extras) continue;
1138                coreSDK[c].GetFullPath(path);
1139                if(c != ide && c != runtime && c != eda && c != ec && c != vanilla)
1140                   PathCat(path, "bin");
1141                MakeSlashPath(path);
1142                if(c == ide)
1143                {
1144                   coreSDK[c].GetFullPath(idePath);
1145                   PathCat(idePath, "IDE.exe");
1146                }
1147
1148                if(c != vanilla)
1149                {
1150                   if(!compiler.executableDirs.Find(path))
1151                      compiler.executableDirs.Add(CopyString(path));
1152                }
1153
1154                if(c == runtime || c == vanilla)
1155                {
1156                   if(!compiler.libraryDirs.Find(path))
1157                      compiler.libraryDirs.Add(CopyString(path));
1158                }
1159             }
1160    #ifndef NOMINGW
1161             // Up to before Win32 APIs
1162             for(c = 0; c < 4 /*additional[c].name*/; c++)
1163             {
1164                char path[MAX_LOCATION];
1165                additional[c].GetFullPath(path);
1166                if(c != 0) // upx
1167                   PathCat(path, "bin");
1168                MakeSlashPath(path);
1169                if(!compiler.executableDirs.Find(path))
1170                   compiler.executableDirs.Add(CopyString(path));
1171             }
1172    #endif
1173
1174             {
1175                char path[MAX_LOCATION] = "";
1176                if(components[samples].selected)
1177                   components[samples].GetFullPath(path);
1178                else
1179                   components[coreSDK].GetFullPath(path);
1180
1181                if(!settings.ideProjectFileDialogLocation)
1182                   settings.ideProjectFileDialogLocation = path;
1183                if(!settings.ideFileDialogLocation)
1184                   settings.ideFileDialogLocation = path;
1185             }
1186          }
1187
1188          settingsContainer.Save();
1189          delete settingsContainer;
1190          delete settings;
1191
1192          // Set up Uninstaller
1193          ((GuiApplication)__thisModule).Lock();
1194          installProgress.installing.text = $"Registering uninstaller...";
1195          ((GuiApplication)__thisModule).Unlock();
1196          ((GuiApplication)__thisModule).SignalEvent();
1197
1198          {
1199             HKEY key;
1200             uint status, size;
1201             char * displayName = "ECERE SDK 0.44";
1202             char uninstaller[MAX_LOCATION];
1203             bool nomodify = true;
1204
1205             strcpy(uninstaller, installDir);
1206             PathCat(uninstaller, "uninstall_ecere.exe");
1207
1208             RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ECERE SDK", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1209
1210             RegSetValueEx(key, "DisplayName", 0, REG_SZ, displayName, (uint)strlen(displayName)+1);
1211             RegSetValueEx(key, "UninstallString", 0, REG_SZ, uninstaller, (uint)strlen(uninstaller)+1);
1212             RegSetValueEx(key, "DisplayIcon", 0, REG_SZ, idePath, (uint)strlen(idePath)+1);
1213             //RegSetValueEx(key, "NoModify", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1214             //RegSetValueEx(key, "NoRepair", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1215             RegCloseKey(key);
1216          }
1217
1218          // Add paths
1219          if(pathOptions[PathOptions::AddECEREPaths].selected 
1220 #ifndef NOMINGW
1221             || pathOptions[PathOptions::AddMinGWPaths].selected
1222 #endif
1223             )
1224          {
1225             HKEY key;
1226             uint status, size;
1227             char path[4096] = "";
1228             uint16 wPath[4096];
1229
1230             ((GuiApplication)__thisModule).Lock();
1231             installProgress.installing.text = "Registering paths...";
1232             ((GuiApplication)__thisModule).Unlock();
1233             ((GuiApplication)__thisModule).SignalEvent();
1234                         
1235             // if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Environment", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1236             
1237             RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1238             // RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1239             if(status == REG_OPENED_EXISTING_KEY)
1240             {
1241                size = sizeof(wPath);
1242                RegQueryValueExW(key, L"path", null, null, (byte *)wPath, &size);
1243                UTF16toUTF8Buffer(wPath, path, sizeof(path));
1244             }
1245             ModifyPath(path);
1246             UTF8toUTF16Buffer(path, wPath, sizeof(wPath) / sizeof(uint16));
1247             RegSetValueExW(key, L"path", 0, REG_EXPAND_SZ, (byte *)wPath, (uint)(wcslen(wPath)+1) * 2);
1248             RegCloseKey(key);
1249
1250             SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, 0, (int)"Environment", SMTO_NORMAL, 1000, NULL);
1251          }
1252
1253          // Install Program Group Icons
1254          GetEnvironment("USERPROFILE", userProfile, sizeof(userProfile));
1255
1256          if(options[IconOptions::StartMenuIcon].selected)
1257          {
1258             char destPath[MAX_LOCATION];
1259             char startMenuPath[MAX_LOCATION] = "";
1260             HKEY key;
1261
1262             ((GuiApplication)__thisModule).Lock();
1263             installProgress.installing.text = $"Installing Start Menu Icons...";
1264             ((GuiApplication)__thisModule).Unlock();
1265             ((GuiApplication)__thisModule).SignalEvent();
1266
1267             strcpy(destPath, userProfile);
1268
1269             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1270             {
1271                uint16 wStartMenuPath[2048];
1272                uint size = sizeof(wStartMenuPath);
1273                // RegQueryValueEx(key, "Start Menu", null, null, startMenuPath, &size);
1274                RegQueryValueExW(key, L"Programs", null, null, (byte *)wStartMenuPath, &size);
1275                UTF16toUTF8Buffer(wStartMenuPath, startMenuPath, sizeof(startMenuPath));
1276                RegCloseKey(key);
1277             }
1278             if(!startMenuPath[0] && userProfile[0])
1279             {
1280                strcpy(startMenuPath, userProfile);
1281                PathCat(startMenuPath, "Start Menu\\Programs");
1282             }
1283
1284             if(startMenuPath[0])
1285             {
1286                strcpy(destPath, startMenuPath);
1287                PathCat(destPath, "ECERE SDK");
1288                MakeDir(destPath);
1289
1290                strcpy(destPath, startMenuPath);
1291                PathCat(destPath, "ECERE SDK\\ECERE IDE.lnk");
1292                CreateLink(idePath, destPath, null); //"ECERE IDE");
1293                if(components[samples].selected)
1294                {
1295                   char samplesPath[MAX_LOCATION] = "";
1296                   components[samples].GetFullPath(samplesPath);
1297
1298                   strcpy(destPath, startMenuPath);
1299                   PathCat(destPath, "ECERE SDK\\Sample Projects.lnk");
1300                   CreateLink(samplesPath, destPath, null);//"Sample Projects");
1301                }
1302                if(components[documentation].selected && documentation[ecereBook].selected)
1303                {
1304                   char docPath[MAX_LOCATION] = "";
1305                   documentation[ecereBook].GetFullPath(docPath);
1306                   PathCat(docPath, "Ecere Tao of Programming [work in progress].pdf");
1307
1308                   {
1309                      char tao[MAX_LOCATION] ;
1310                      documentation[ecereBook].GetFullPath(tao);
1311                      PathCat(tao, "tao.pdf");
1312                      RenameFile(tao, docPath);
1313                   }
1314
1315                   strcpy(destPath, startMenuPath);
1316                   PathCat(destPath, "ECERE SDK\\The Ecere Tao of Programming.lnk");
1317                   CreateLink(docPath, destPath, null);
1318                }
1319             }
1320          }
1321
1322          // Install Desktop Icon
1323          if(options[IconOptions::DesktopIcon].selected)
1324          {
1325             HKEY key;
1326             char desktopPath[MAX_LOCATION];
1327
1328             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1329             {
1330                uint16 wDesktopPath[MAX_LOCATION];
1331                uint size = sizeof(wDesktopPath);
1332                RegQueryValueExW(key, L"Desktop", null, null, (byte *)wDesktopPath, &size);
1333                UTF16toUTF8Buffer(wDesktopPath, desktopPath, sizeof(desktopPath));
1334                RegCloseKey(key);
1335             }
1336             if(!desktopPath[0] && userProfile[0])
1337             {
1338                strcpy(desktopPath, userProfile);
1339                PathCat(desktopPath, "Desktop");
1340             }
1341
1342             if(desktopPath[0])
1343             {
1344                PathCat(desktopPath, "ECERE IDE.lnk");
1345
1346                ((GuiApplication)__thisModule).Lock();
1347                installProgress.installing.text = $"Installing Desktop Icon...";
1348                ((GuiApplication)__thisModule).Unlock();
1349                ((GuiApplication)__thisModule).SignalEvent();
1350
1351                CreateLink(idePath, desktopPath, null);//"ECERE IDE");
1352             }
1353          }
1354
1355          // Install QuickLaunch Icon
1356          if(options[IconOptions::QuickLaunchIcon].selected)
1357          {
1358             char appData[MAX_LOCATION];
1359             GetEnvironment("APPDATA", appData, sizeof(appData));
1360
1361             if(appData[0])
1362             {
1363                char destPath[MAX_LOCATION];
1364
1365                ((GuiApplication)__thisModule).Lock();
1366                installProgress.installing.text = $"Installing Quicklaunch Icon...";
1367                ((GuiApplication)__thisModule).Unlock();
1368                ((GuiApplication)__thisModule).SignalEvent();
1369
1370                strcpy(destPath, appData);
1371                PathCat(destPath, "Microsoft\\Internet Explorer\\Quick Launch\\ECERE IDE.lnk");
1372
1373                CreateLink(idePath, destPath, null);//"ECERE IDE");
1374             }
1375          }
1376
1377          // Register File Types
1378          if(associateOptions[AssociateOptions::AssociateEPJ].selected ||
1379             associateOptions[AssociateOptions::AssociateEC].selected ||
1380             associateOptions[AssociateOptions::AssociateC].selected ||
1381             associateOptions[AssociateOptions::AssociateCPP].selected ||
1382             associateOptions[AssociateOptions::AssociateTXT].selected ||
1383             associateOptions[AssociateOptions::Associate3DS].selected ||
1384             associateOptions[AssociateOptions::AssociateIMG].selected)
1385          {
1386             ((GuiApplication)__thisModule).Lock();
1387             installProgress.installing.text = $"Resgistering File Types...";
1388             ((GuiApplication)__thisModule).Unlock();
1389             ((GuiApplication)__thisModule).SignalEvent();
1390             
1391             if(associateOptions[AssociateOptions::AssociateEPJ].selected)
1392             {
1393                AssociateExtension(".epj", "ECERE IDE Project", "epj_file", "Open", idePath);
1394             }
1395             if(associateOptions[AssociateOptions::AssociateEC].selected)
1396             {
1397                AssociateExtension(".ec", "eC Source File", "ec_file", "Open", idePath);
1398                AssociateExtension(".eh", "eC Header File", "eh_file", "Open", idePath);
1399             }
1400             if(associateOptions[AssociateOptions::AssociateC].selected)
1401             {
1402                AssociateExtension(".c", "C Source File", "c_file", "Open", idePath);
1403                AssociateExtension(".h", "C Header File", "h_file", "Open", idePath);
1404             }
1405             if(associateOptions[AssociateOptions::AssociateCPP].selected)
1406             {
1407                AssociateExtension(".cpp", "C++ Source File", "cpp_file", "Open", idePath);
1408                AssociateExtension(".cc", "C++ Source File", "cpp_file", "Open", idePath);
1409                AssociateExtension(".cxx", "C++ Source File", "cpp_file", "Open", idePath);
1410                AssociateExtension(".chh", "C++ Header File", "chh_file", "Open", idePath);
1411                AssociateExtension(".hh", "C++ Header File", "chh_file", "Open", idePath);
1412                AssociateExtension(".hxx", "C++ Header File", "chh_file", "Open", idePath);
1413             }
1414             if(associateOptions[AssociateOptions::AssociateTXT].selected)
1415             {
1416                AssociateExtension(".txt", "Text File", "txt_file", "Open", idePath);
1417             }
1418             if(associateOptions[AssociateOptions::Associate3DS].selected)
1419             {
1420                AssociateExtension(".3ds", "3D Studio Model", "3ds_file", "View", idePath);
1421             }
1422             if(associateOptions[AssociateOptions::AssociateIMG].selected)
1423             {
1424                AssociateExtension(".bmp", "BMP Image", "bmp_file", "View", idePath);
1425                AssociateExtension(".pcx", "PCX Image", "pcx_file", "View", idePath);
1426                AssociateExtension(".png", "PNG Image", "png_file", "View", idePath);
1427                AssociateExtension(".jpg", "JPEG Image", "jpg_file", "View", idePath);
1428                AssociateExtension(".jpeg", "JPEG Image", "jpg_file", "View", idePath);
1429                AssociateExtension(".gif", "GIF Image", "gif_file", "View", idePath);
1430             }
1431          }
1432          ((GuiApplication)__thisModule).Lock();
1433
1434          installProgress.cancel.Destroy(0);
1435          installProgress.finish.text = $"Finish";
1436          installProgress.finish.disabled = false;
1437          installProgress.finish.Activate();
1438          installProgress.installing.text = $"Installation Complete.";
1439          installProgress.title.text = $"Installation Complete";
1440          installProgress.titleInfo.contents = $"Thank you for using the Ecere SDK.";
1441       }
1442       ((GuiApplication)__thisModule).Unlock();
1443       return 0;
1444    }
1445 }