installer: 32/64 bit Installer with TDM-MinGW-w64
[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             osIS64bit = true;
860
861             *x86 = 0;
862             PathCat(installDir, "Ecere SDK");
863             strcpy(installDir, programFilesDir);
864          }
865          else
866          {
867             PathCat(installDir, "Ecere SDK");
868             strcpy(installDir, programFilesDir);
869          }
870       }
871       else if(homeDrive && homeDrive[0])
872       {
873          strcpy(installDir, homeDrive);
874          PathCat(installDir, "Ecere SDK");
875       }
876       else if(winDir && winDir[0])
877       {
878          strcpy(installDir, winDir);
879          PathCat(installDir, "..\\Ecere SDK");
880       }
881       else
882          strcpy(installDir, "C:\\Ecere SDK");
883       
884       if(appData && appData[0])
885       {
886          static char defSamplesPath[MAX_LOCATION];
887          static char defExtrasPath[MAX_LOCATION];
888          strcpy(defSamplesPath, appData);
889          PathCat(defSamplesPath, "Ecere SDK\\Samples");
890          components[samples].defInstallPath = defSamplesPath;
891
892          strcpy(defExtrasPath, appData);
893          PathCat(defExtrasPath, "Ecere SDK\\extras");
894          additional[extras].defInstallPath = defExtrasPath;
895       }
896          
897       destBox.path = installDir;
898
899       {
900          ComponentID c;
901          for(c = 0; components[c].name; c++)
902             AddComponent(components[c], null, installDir);
903       }
904
905       // Compute size apart because ToggleCheck will change it
906       totalSize = 0;
907       {
908          ComponentID c;
909          for(c = 0; components[c].name; c++)
910             totalSize += components[c].requiredSize;
911       }
912       {
913          char sizeString[100];
914          PrintSize(sizeString, totalSize, 2);
915          totalSpaceValue.text = sizeString;
916       }
917       for(c = 0; options[c].name; c++)
918          AddOption(options[c], null);
919    }
920
921    bool OnCreate()
922    {
923       destBox.Activate();
924       return true;
925    }
926
927    void OnDrawOverChildren(Surface surface)
928    {
929       int tw = label2.size.w;
930       surface.SetForeground(Color { 128, 128, 128 });
931       surface.HLine(label2.position.x + tw + 6, 620, 400);
932       surface.SetForeground(white);
933       surface.HLine(label2.position.x + tw + 6, 621, 401);
934       surface.PutPixel(621, 400);
935    }
936
937    Label label3
938    {
939       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
940    };
941 }
942
943 class InstallProgress : Window
944 {
945    text = $"Ecere Software Development Kit Setup - v0.44.08 \"Ryōan-ji\" 64 Bit Edition";
946    background = activeBorder;
947    borderStyle = fixed;
948    hasMinimize = true;
949    hasClose = true;
950    tabCycle = true;
951    // size = Size { 640, 480 };
952    clientSize = { 636, 456 };
953    //clientSize = { 796, 576 };
954    icon = { ":icon.png" };
955
956    Picture back { image = BitmapResource { ":ryoanji-progress.png" }, parent = this, position = { 0, 0 } };
957    Label installing { this, position = { 32, 160 } };
958    ProgressBar progressBar { parent = this, size = Size { 588, 24 }, anchor = Anchor { left = 24, top = 184 } };
959    Button finish
960    {
961       parent = this, text = $"Install", disabled = true, isDefault = true, size = Size { 75, 23 }, anchor = Anchor { left = 432, top = 416 };
962
963       NotifyClicked = ButtonCloseDialog
964    };
965    Button cancel
966    {
967       this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 };
968
969       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
970       {
971          abortInstall = true;
972          return true;
973       }
974    };
975    EditBox titleInfo
976    {
977       inactive = true, noSelect = true,
978       multiLine = true, parent = label3, opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
979       contents = $"Please wait while the Ecere Software Development Kit is being installed."
980    };
981    Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
982    Picture picture1
983    {
984       image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, anchor = Anchor { left = 16, top = 4 };
985       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
986
987       bool OnLeftButtonDown(int x, int y, Modifiers mods)
988       {
989          ShellOpen("http://www.ecere.com/");
990          return true;
991       }
992    };
993    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 } };
994
995    void OnDrawOverChildren(Surface surface)
996    {
997       int tw = label2.size.w;
998       surface.SetForeground(Color { 128, 128, 128 });
999       surface.HLine(label2.position.x + tw + 6, 620, 400);
1000       surface.SetForeground(white);
1001       surface.HLine(label2.position.x + tw + 6, 621, 401);
1002       surface.PutPixel(621, 400);
1003    }
1004
1005    Label label3
1006    {
1007       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
1008    };
1009    InstallThread thread
1010    {
1011    };
1012 }
1013
1014 Installer installer {};
1015 InstallProgress installProgress { autoCreate = false };
1016
1017 static void AddPath(char * sysPaths[200], int sysCount, char * paths[200], int * count, char * oldPath, char * userPath, char * path)
1018 {
1019    int p;
1020    bool found = false;
1021    for(p = 0; p<sysCount; p++)
1022       if(!fstrcmp(sysPaths[p], path))
1023       {
1024          found = true;
1025          break;
1026       }
1027    for(p = 0; !found && p<*count; p++)
1028       if(!fstrcmp(paths[p], path))
1029       {
1030          found = true;
1031          break;
1032       }
1033    if(!found)
1034    {
1035       char * start;
1036       if(*count) 
1037       {
1038          strcat(userPath, ";");
1039          start = paths[(*count)-1] + strlen(paths[(*count)-1])+1;
1040       }
1041       else
1042          start = oldPath;
1043
1044       strcpy(start, path);
1045       *(start + strlen(path)) = '\0';
1046       paths[(*count)++] = start;
1047
1048       strcat(userPath, path);
1049    }
1050 }
1051
1052
1053 void ModifyPath(char * systemPath, char * userPath)
1054 {
1055    char oldPath[8192], * paths[200], * sysPaths[200];
1056    int count, sysCount;
1057
1058    strcpy(oldPath, userPath);
1059    count = TokenizeWith(oldPath, sizeof(paths) / sizeof(char *), paths, ";", false);
1060    sysCount = TokenizeWith(systemPath, sizeof(sysPaths) / sizeof(char *), sysPaths, ";", false);
1061
1062    {
1063       CoreSDKID c;
1064       for(c = 0; coreSDK[c].name; c++)
1065       {
1066          bool found = false;
1067          char path[MAX_LOCATION];
1068          if(!coreSDK[c].selected) continue;
1069          coreSDK[c].GetFullPath(path);
1070          if(c != ide && c != runtime && c != ec &&
1071             c != ide32 && c != runtime32 && c != ec32)
1072          {
1073             if(!pathOptions[PathOptions::AddMinGWPaths].available || !pathOptions[PathOptions::AddMinGWPaths].selected)
1074                continue;
1075             PathCat(path, "bin");
1076          }
1077          else if(!pathOptions[PathOptions::AddECEREPaths].selected) continue;
1078
1079          AddPath(sysPaths, sysCount, paths, &count, oldPath, userPath, path);
1080       }
1081    }
1082    {
1083       AdditionalID c;
1084       // Up to C++
1085       for(c = 0; c <= cpp; c++)
1086       {
1087          bool found = false;
1088          char path[MAX_LOCATION];
1089          if(!additional[c].selected || c == vanilla || c == vanilla32 || c == extras) continue;
1090          if((c != eda && c != eda32 && c != upx) && (!pathOptions[PathOptions::AddMinGWPaths].available || !pathOptions[PathOptions::AddMinGWPaths].selected))
1091             continue;
1092          additional[c].GetFullPath(path);
1093          if(c != eda && c != eda32 && c != upx)
1094             PathCat(path, "bin");
1095          AddPath(sysPaths, sysCount, paths, &count, oldPath, userPath, path);
1096       }
1097    }
1098 }
1099
1100 void AssociateExtension(char * extension, char * description, char *name, char * action, char * path)
1101 {
1102    HKEY key;
1103    uint status, size;
1104    char keyName[1024];
1105
1106    RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1107    RegSetValueEx(key, null, 0, REG_SZ, name, (uint)strlen(name)+1);
1108    RegCloseKey(key);
1109
1110    RegCreateKeyEx(HKEY_CLASSES_ROOT, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1111    RegSetValueEx(key, null, 0, REG_SZ, description, (uint)strlen(description)+1);
1112    RegCloseKey(key);
1113
1114    sprintf(keyName, "%s\\shell", extension);
1115    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1116    RegSetValueEx(key, null, 0, REG_SZ, action, (uint)strlen(action)+1);
1117    RegCloseKey(key);
1118
1119    sprintf(keyName, "%s\\shell\\%s", name, action);
1120    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1121    RegCloseKey(key);
1122
1123    sprintf(keyName, "%s\\shell\\%s\\command", name, action);
1124    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1125    
1126    sprintf(keyName, path);
1127    strcat(keyName, " \"%L\"");
1128    {
1129       uint16 wKeyName[2048];
1130       UTF8toUTF16Buffer(keyName, wKeyName, sizeof(wKeyName) / sizeof(uint16));
1131       RegSetValueExW(key, null, 0, REG_SZ, (byte *)wKeyName, (uint)(wcslen(wKeyName) + 1)*sizeof(uint16));
1132    }
1133    RegCloseKey(key);
1134 }
1135
1136 class InstallThread : Thread
1137 {
1138    unsigned int Main()
1139    {
1140       ComponentID c;
1141       ((GuiApplication)__thisModule).Lock();
1142       installProgress.progressBar.range = totalSize;
1143       for(c = 0; components[c].name && !abortInstall; c++)
1144          components[c].Install(installDir, installDir32);
1145       if(abortInstall)
1146       {
1147          installProgress.progressBar.range = 0;
1148          installProgress.finish.Destroy(0);
1149          installProgress.cancel.text = $"Close";
1150          installProgress.cancel.isDefault = true;
1151          installProgress.cancel.disabled = false;
1152          installProgress.cancel.NotifyClicked = Window::ButtonCloseDialog;
1153          installProgress.installing.text = $"Installation Cancelled.";
1154          installProgress.title.text = $"Installation Cancelled";
1155          installProgress.titleInfo.contents = $"The installation was not completed.";
1156       }
1157       else
1158       {
1159          char idePath[MAX_LOCATION];
1160          char userProfile[MAX_LOCATION];
1161
1162          // Configure IDE
1163          IDESettings settings = null; // Don't instantiate yet so we can pick up old settings
1164
1165          IDESettingsContainer settingsContainer
1166          {
1167             driver = "JSON";
1168             dataOwner = &settings;
1169             dataClass = class(IDESettings);
1170          };
1171          CompilerConfig compiler;
1172          installProgress.installing.text = $"Configuring Ecere IDE...";
1173          ((GuiApplication)__thisModule).Unlock();
1174          ((GuiApplication)__thisModule).SignalEvent();
1175
1176          settingsContainer.Load();
1177          compiler = settings.GetCompilerConfig(defaultCompilerName);
1178          if(compiler)
1179          {
1180             {
1181                CoreSDKID c;
1182                for(c = 0; coreSDK[c].name; c++)
1183                {
1184                   char path[MAX_LOCATION];
1185                   if(!coreSDK[c].selected || !coreSDK[c].available) continue;
1186
1187                   coreSDK[c].GetFullPath(path);
1188                   if(c != ide && c != runtime && c != ec &&
1189                      c != ide32 && c != runtime32 && c != ec32)
1190                      PathCat(path, "bin");
1191                   MakeSlashPath(path);
1192                   if((c == ide && osIS64bit) || (c == ide32 && !osIS64bit))
1193                   {
1194                      coreSDK[c].GetFullPath(idePath);
1195                      PathCat(idePath, "ide.exe");
1196                   }
1197
1198                   if(c == runtime || c == runtime32)
1199                   {
1200                      if(!compiler.libraryDirs.Find(path))
1201                         compiler.libraryDirs.Add(CopyString(path));
1202                   }
1203                }
1204             }
1205             {
1206                AdditionalID c;
1207                // Up to C++
1208                for(c = 0; c <= cpp; c++)
1209                {
1210                   char path[MAX_LOCATION];
1211                   if(c == extras || !additional[c].selected || !additional[c].available) continue;
1212                   additional[c].GetFullPath(path);
1213                   if(c != upx && c != eda && c != vanilla && c != eda32 && c != vanilla32)
1214                      PathCat(path, "bin");
1215                   MakeSlashPath(path);
1216                   if(c == vanilla || c == vanilla32)
1217                   {
1218                      if(!compiler.libraryDirs.Find(path))
1219                         compiler.libraryDirs.Add(CopyString(path));
1220                   }
1221                   else
1222                   {
1223                      if(!compiler.executableDirs.Find(path))
1224                         compiler.executableDirs.Add(CopyString(path));
1225                   }
1226                }
1227             }
1228          }
1229
1230          {
1231             char path[MAX_LOCATION] = "";
1232
1233             if(components[samples].selected)
1234                components[samples].GetFullPath(path);
1235             else
1236                components[coreSDK].GetFullPath(path);
1237
1238             if(!settings.ideProjectFileDialogLocation[0])
1239                settings.ideProjectFileDialogLocation = path;
1240             if(!settings.ideFileDialogLocation[0])
1241                settings.ideFileDialogLocation = path;
1242
1243             if(documentation[apiRef].selected)
1244             {
1245                documentation[apiRef].GetFullPath(path);
1246                if(!settings.docDir[0])
1247                   settings.docDir = path;
1248             }
1249          }
1250
1251          settingsContainer.Save();
1252          delete settingsContainer;
1253          delete settings;
1254
1255          // Set up Uninstaller
1256          ((GuiApplication)__thisModule).Lock();
1257          installProgress.installing.text = $"Registering uninstaller...";
1258          ((GuiApplication)__thisModule).Unlock();
1259          ((GuiApplication)__thisModule).SignalEvent();
1260
1261          {
1262             HKEY key;
1263             uint status, size;
1264             char * displayName = "Ecere SDK 0.44";
1265             char uninstaller[MAX_LOCATION];
1266             bool nomodify = true;
1267
1268             strcpy(uninstaller, installDir);
1269             PathCat(uninstaller, "uninstall_ecere.exe");
1270
1271             RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Ecere SDK", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1272
1273             RegSetValueEx(key, "DisplayName", 0, REG_SZ, displayName, (uint)strlen(displayName)+1);
1274             RegSetValueEx(key, "UninstallString", 0, REG_SZ, uninstaller, (uint)strlen(uninstaller)+1);
1275             RegSetValueEx(key, "DisplayIcon", 0, REG_SZ, idePath, (uint)strlen(idePath)+1);
1276             //RegSetValueEx(key, "NoModify", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1277             //RegSetValueEx(key, "NoRepair", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1278             RegCloseKey(key);
1279          }
1280
1281          // Add paths
1282          if(pathOptions[PathOptions::AddECEREPaths].selected 
1283 #ifndef NOMINGW
1284             || pathOptions[PathOptions::AddMinGWPaths].selected
1285 #endif
1286             )
1287          {
1288             HKEY userKey = null, systemKey = null;
1289             uint status, size;
1290             char userPath[8192] = "";
1291             char systemPath[8192] = "";
1292             uint16 wUserPath[8192];
1293             uint16 wSystemPath[8192];
1294
1295             wUserPath[0] = 0;
1296             wSystemPath[0] = 0;
1297
1298             ((GuiApplication)__thisModule).Lock();
1299             installProgress.installing.text = "Registering paths...";
1300             ((GuiApplication)__thisModule).Unlock();
1301             ((GuiApplication)__thisModule).SignalEvent();
1302                         
1303             if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, KEY_QUERY_VALUE, &systemKey) == ERROR_SUCCESS)
1304             {
1305                size = sizeof(wSystemPath);
1306                RegQueryValueExW(systemKey, L"path", null, null, (byte *)wSystemPath, &size);
1307                UTF16toUTF8Buffer(wSystemPath, systemPath, sizeof(systemPath));
1308             }
1309             
1310             RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &userKey, &status);
1311             if(status == REG_OPENED_EXISTING_KEY)
1312             {
1313                size = sizeof(wUserPath);
1314                RegQueryValueExW(userKey, L"path", null, null, (byte *)wUserPath, &size);
1315                UTF16toUTF8Buffer(wUserPath, userPath, sizeof(userPath));
1316             }
1317             ModifyPath(systemPath, userPath);
1318             UTF8toUTF16Buffer(userPath, wUserPath, sizeof(wUserPath) / sizeof(uint16));
1319             RegSetValueExW(userKey, L"path", 0, REG_EXPAND_SZ, (byte *)wUserPath, (uint)(wcslen(wUserPath)+1) * 2);
1320             RegCloseKey(userKey);
1321             RegCloseKey(systemKey);
1322
1323             SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, 0, (int)"Environment", SMTO_NORMAL, 1000, NULL);
1324          }
1325
1326          // Install Program Group Icons
1327          GetEnvironment("USERPROFILE", userProfile, sizeof(userProfile));
1328
1329          if(options[IconOptions::StartMenuIcon].selected)
1330          {
1331             char destPath[MAX_LOCATION];
1332             char startMenuPath[MAX_LOCATION] = "";
1333             HKEY key;
1334
1335             ((GuiApplication)__thisModule).Lock();
1336             installProgress.installing.text = $"Installing Start Menu Icons...";
1337             ((GuiApplication)__thisModule).Unlock();
1338             ((GuiApplication)__thisModule).SignalEvent();
1339
1340             strcpy(destPath, userProfile);
1341
1342             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1343             {
1344                uint16 wStartMenuPath[2048];
1345                uint size = sizeof(wStartMenuPath);
1346                // RegQueryValueEx(key, "Start Menu", null, null, startMenuPath, &size);
1347                RegQueryValueExW(key, L"Programs", null, null, (byte *)wStartMenuPath, &size);
1348                UTF16toUTF8Buffer(wStartMenuPath, startMenuPath, sizeof(startMenuPath));
1349                RegCloseKey(key);
1350             }
1351             if(!startMenuPath[0] && userProfile[0])
1352             {
1353                strcpy(startMenuPath, userProfile);
1354                PathCat(startMenuPath, "Start Menu\\Programs");
1355             }
1356
1357             if(startMenuPath[0])
1358             {
1359                strcpy(destPath, startMenuPath);
1360                PathCat(destPath, "Ecere SDK");
1361                MakeDir(destPath);
1362
1363                strcpy(destPath, startMenuPath);
1364                PathCat(destPath, "Ecere SDK\\Ecere IDE.lnk");
1365                CreateLink(idePath, destPath, null); //"Ecere IDE");
1366                if(components[samples].selected)
1367                {
1368                   char samplesPath[MAX_LOCATION] = "";
1369                   components[samples].GetFullPath(samplesPath);
1370
1371                   strcpy(destPath, startMenuPath);
1372                   PathCat(destPath, "Ecere SDK\\Sample Projects.lnk");
1373                   CreateLink(samplesPath, destPath, null);//"Sample Projects");
1374                }
1375                if(components[documentation].selected && documentation[ecereBook].selected)
1376                {
1377                   char docPath[MAX_LOCATION] = "";
1378                   documentation[ecereBook].GetFullPath(docPath);
1379                   PathCat(docPath, "Ecere Tao of Programming [work in progress].pdf");
1380
1381                   {
1382                      char tao[MAX_LOCATION] ;
1383                      documentation[ecereBook].GetFullPath(tao);
1384                      PathCat(tao, "tao.pdf");
1385                      RenameFile(tao, docPath);
1386                   }
1387
1388                   strcpy(destPath, startMenuPath);
1389                   PathCat(destPath, "Ecere SDK\\The Ecere Tao of Programming.lnk");
1390                   CreateLink(docPath, destPath, null);
1391                }
1392             }
1393          }
1394
1395          // Install Desktop Icon
1396          if(options[IconOptions::DesktopIcon].selected)
1397          {
1398             HKEY key;
1399             char desktopPath[MAX_LOCATION];
1400
1401             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1402             {
1403                uint16 wDesktopPath[MAX_LOCATION];
1404                uint size = sizeof(wDesktopPath);
1405                RegQueryValueExW(key, L"Desktop", null, null, (byte *)wDesktopPath, &size);
1406                UTF16toUTF8Buffer(wDesktopPath, desktopPath, sizeof(desktopPath));
1407                RegCloseKey(key);
1408             }
1409             if(!desktopPath[0] && userProfile[0])
1410             {
1411                strcpy(desktopPath, userProfile);
1412                PathCat(desktopPath, "Desktop");
1413             }
1414
1415             if(desktopPath[0])
1416             {
1417                PathCat(desktopPath, "Ecere IDE.lnk");
1418
1419                ((GuiApplication)__thisModule).Lock();
1420                installProgress.installing.text = $"Installing Desktop Icon...";
1421                ((GuiApplication)__thisModule).Unlock();
1422                ((GuiApplication)__thisModule).SignalEvent();
1423
1424                CreateLink(idePath, desktopPath, null);//"Ecere IDE");
1425             }
1426          }
1427
1428          // Install QuickLaunch Icon
1429          if(options[IconOptions::QuickLaunchIcon].selected)
1430          {
1431             char appData[MAX_LOCATION];
1432             GetEnvironment("APPDATA", appData, sizeof(appData));
1433
1434             if(appData[0])
1435             {
1436                char destPath[MAX_LOCATION];
1437
1438                ((GuiApplication)__thisModule).Lock();
1439                installProgress.installing.text = $"Installing Quicklaunch Icon...";
1440                ((GuiApplication)__thisModule).Unlock();
1441                ((GuiApplication)__thisModule).SignalEvent();
1442
1443                strcpy(destPath, appData);
1444                PathCat(destPath, "Microsoft\\Internet Explorer\\Quick Launch\\Ecere IDE.lnk");
1445
1446                CreateLink(idePath, destPath, null);//"Ecere IDE");
1447             }
1448          }
1449
1450          // Register File Types
1451          if(associateOptions[AssociateOptions::AssociateEPJ].selected ||
1452             associateOptions[AssociateOptions::AssociateEC].selected ||
1453             associateOptions[AssociateOptions::AssociateC].selected ||
1454             associateOptions[AssociateOptions::AssociateCPP].selected ||
1455             associateOptions[AssociateOptions::AssociateTXT].selected ||
1456             associateOptions[AssociateOptions::Associate3DS].selected ||
1457             associateOptions[AssociateOptions::AssociateIMG].selected)
1458          {
1459             ((GuiApplication)__thisModule).Lock();
1460             installProgress.installing.text = $"Resgistering File Types...";
1461             ((GuiApplication)__thisModule).Unlock();
1462             ((GuiApplication)__thisModule).SignalEvent();
1463             
1464             if(associateOptions[AssociateOptions::AssociateEPJ].selected)
1465             {
1466                AssociateExtension(".epj", "Ecere IDE Project", "epj_file", "Open", idePath);
1467             }
1468             if(associateOptions[AssociateOptions::AssociateEC].selected)
1469             {
1470                AssociateExtension(".ec", "eC Source File", "ec_file", "Open", idePath);
1471                AssociateExtension(".eh", "eC Header File", "eh_file", "Open", idePath);
1472             }
1473             if(associateOptions[AssociateOptions::AssociateC].selected)
1474             {
1475                AssociateExtension(".c", "C Source File", "c_file", "Open", idePath);
1476                AssociateExtension(".h", "C Header File", "h_file", "Open", idePath);
1477             }
1478             if(associateOptions[AssociateOptions::AssociateCPP].selected)
1479             {
1480                AssociateExtension(".cpp", "C++ Source File", "cpp_file", "Open", idePath);
1481                AssociateExtension(".cc", "C++ Source File", "cpp_file", "Open", idePath);
1482                AssociateExtension(".cxx", "C++ Source File", "cpp_file", "Open", idePath);
1483                AssociateExtension(".chh", "C++ Header File", "chh_file", "Open", idePath);
1484                AssociateExtension(".hh", "C++ Header File", "chh_file", "Open", idePath);
1485                AssociateExtension(".hxx", "C++ Header File", "chh_file", "Open", idePath);
1486             }
1487             if(associateOptions[AssociateOptions::AssociateTXT].selected)
1488             {
1489                AssociateExtension(".txt", "Text File", "txt_file", "Open", idePath);
1490             }
1491             if(associateOptions[AssociateOptions::Associate3DS].selected)
1492             {
1493                AssociateExtension(".3ds", "3D Studio Model", "3ds_file", "View", idePath);
1494             }
1495             if(associateOptions[AssociateOptions::AssociateIMG].selected)
1496             {
1497                AssociateExtension(".bmp", "BMP Image", "bmp_file", "View", idePath);
1498                AssociateExtension(".pcx", "PCX Image", "pcx_file", "View", idePath);
1499                AssociateExtension(".png", "PNG Image", "png_file", "View", idePath);
1500                AssociateExtension(".jpg", "JPEG Image", "jpg_file", "View", idePath);
1501                AssociateExtension(".jpeg", "JPEG Image", "jpg_file", "View", idePath);
1502                AssociateExtension(".gif", "GIF Image", "gif_file", "View", idePath);
1503             }
1504          }
1505          ((GuiApplication)__thisModule).Lock();
1506
1507          installProgress.cancel.Destroy(0);
1508          installProgress.finish.text = $"Finish";
1509          installProgress.finish.disabled = false;
1510          installProgress.finish.Activate();
1511          installProgress.installing.text = $"Installation Complete.";
1512          installProgress.title.text = $"Installation Complete";
1513          installProgress.titleInfo.contents = $"Thank you for using the Ecere SDK.";
1514       }
1515       ((GuiApplication)__thisModule).Unlock();
1516       return 0;
1517    }
1518 }