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