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