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