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