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