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