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