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