installer: Installing extras along samples in %APPDATA%
[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          static char defExtrasPath[MAX_LOCATION];
945          strcpy(defSamplesPath, appData);
946          PathCat(defSamplesPath, "ECERE SDK\\Samples");
947          components[samples].defInstallPath = defSamplesPath;
948
949          strcpy(defExtrasPath, appData);
950          PathCat(defExtrasPath, "ECERE SDK\\extras");
951          coreSDK[extras].defInstallPath = defExtrasPath;
952       }
953          
954       destBox.contents = installDir;
955
956       {
957          ComponentID c;
958          for(c = 0; components[c].name; c++)
959             AddComponent(components[c], null, installDir);
960       }
961
962       // Compute size apart because ToggleCheck will change it
963       totalSize = 0;
964       {
965          ComponentID c;
966          for(c = 0; components[c].name; c++)
967             totalSize += components[c].requiredSize;
968       }
969       {
970          char sizeString[100];
971          PrintSize(sizeString, totalSize, 2);
972          totalSpaceValue.text = sizeString;
973       }
974       for(c = 0; options[c].name; c++)
975          AddOption(options[c], null);
976    }
977
978    bool OnCreate()
979    {
980       destBox.Activate();
981       return true;
982    }
983
984    void OnRedraw(Surface surface)
985    {
986       ColorKey keys[2] =
987       {
988          { blue, 0 },
989          { darkBlue, 1 }
990       };
991       //surface.Gradient(keys, sizeof(keys)/sizeof(ColorKey), 1.0f, Vertical, 0,0, clientSize.w, clientSize.h);
992       surface.SetForeground(Color { 128, 128, 128 });
993       surface.HLine(160, 620, 400);
994       surface.SetForeground(white);
995       surface.HLine(160, 621, 401);
996       surface.PutPixel(621, 400);
997    }
998    Label label3
999    {
1000       parent = this, opacity = 1, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
1001
1002       void OnRedraw(Surface surface)
1003       {
1004          ColorKey keys[] =
1005          {
1006             { white, 0 },
1007             { activeBorder, 1 }
1008          };
1009          surface.Gradient(keys, sizeof(keys)/sizeof(ColorKey), 0, horizontal, 220,0, clientSize.w, clientSize.h);
1010
1011          Label::OnRedraw(surface);
1012       }
1013    };
1014 }
1015
1016 class InstallProgress : Window
1017 {
1018    text = $"Ecere Software Development Kit Setup";
1019    background = activeBorder;
1020    borderStyle = fixed;
1021    hasMinimize = true;
1022    hasClose = true;
1023    tabCycle = true;
1024    size = Size { 640, 480 };
1025
1026    Label installing { this, position = { 32, 160 } };
1027    ProgressBar progressBar { parent = this, size = Size { 588, 24 }, anchor = Anchor { left = 24, top = 184 } };
1028    Button finish
1029    {
1030       parent = this, text = $"Install", disabled = true, isDefault = true, size = Size { 75, 23 }, anchor = Anchor { left = 432, top = 416 };
1031
1032       NotifyClicked = ButtonCloseDialog
1033    };
1034    Button cancel
1035    {
1036       this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 };
1037
1038       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1039       {
1040          abortInstall = true;
1041          return true;
1042       }
1043    };
1044    EditBox titleInfo
1045    {
1046       inactive = true, noSelect = true,
1047       multiLine = true, parent = label3, opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
1048       contents = $"Please wait while the Ecere Software Development Kit is being installed."
1049    };
1050    Label label2 { parent = this, text = buildString, position = { 16, 392 }, disabled = true, opacity = 1, background = activeBorder };
1051    Picture picture1 { image = BitmapResource { ":ecere.bmp", transparent = true }, filter = true, parent = label3, anchor = Anchor { left = 16, top = 4 } };
1052    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 } };
1053
1054    void OnRedraw(Surface surface)
1055    {
1056       ColorKey keys[2] =
1057       {
1058          { blue, 0 },
1059          { darkBlue, 1 }
1060       };
1061       //surface.Gradient(keys, sizeof(keys)/sizeof(ColorKey), 1.0f, Vertical, 0,0, clientSize.w, clientSize.h);
1062       surface.SetForeground(Color { 128, 128, 128 });
1063       surface.HLine(160, 620, 400);
1064       surface.SetForeground(white);
1065       surface.HLine(160, 621, 401);
1066       surface.PutPixel(621, 400);
1067    }
1068    Label label3
1069    {
1070       parent = this, opacity = 1, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
1071
1072       void OnRedraw(Surface surface)
1073       {
1074          ColorKey keys[] =
1075          {
1076             { white, 0 },
1077             { activeBorder, 1 }
1078          };
1079          surface.Gradient(keys, sizeof(keys)/sizeof(ColorKey), 0, horizontal, 220,0, clientSize.w, clientSize.h);
1080
1081          Label::OnRedraw(surface);
1082       }
1083    };
1084    InstallThread thread
1085    {
1086    };
1087 }
1088
1089 Installer installer {};
1090 InstallProgress installProgress { autoCreate = false };
1091
1092 void ModifyPath(char * newPath)
1093 {
1094    char * paths[100];
1095    int p, count;
1096    char oldPath[4096];
1097    CoreSDKID c;
1098
1099    strcpy(oldPath, newPath);
1100    count = TokenizeWith(oldPath, sizeof(paths) / sizeof(char *), paths, ";", false);
1101
1102    for(c = 0; coreSDK[c].name; c++)
1103    {
1104       char path[MAX_LOCATION];
1105       coreSDK[c].GetFullPath(path);
1106       if(c != ide && c != runtime && c != eda && c != ec)
1107       {
1108 #ifndef NOMINGW
1109          if(!pathOptions[PathOptions::AddMinGWPaths].selected)
1110 #endif
1111          continue;
1112          PathCat(path, "bin");
1113       }
1114       else if(!pathOptions[PathOptions::AddECEREPaths].selected) continue;
1115
1116       for(p = 0; p<count; p++)
1117          if(!fstrcmp(paths[p], path))
1118             break;
1119       if(p == count)
1120       {
1121          char * start;
1122          if(count) 
1123          {
1124             strcat(newPath, ";");
1125             start = oldPath + strlen(paths[count-1])+1;
1126          }
1127          else
1128             start = oldPath;
1129          
1130          strcpy(start, path);
1131          *(start + strlen(path)) = '\0';
1132          paths[count++] = start;
1133
1134          strcat(newPath, path);
1135       }
1136    }
1137 #ifndef NOMINGW
1138    if(pathOptions[PathOptions::AddMinGWPaths].selected)
1139    {
1140       int c;
1141       for(c = 0; additional[c].name; c++)
1142       {
1143          char path[MAX_LOCATION];
1144          NamedItem item;
1145          additional[c].GetFullPath(path);
1146          PathCat(path, "bin");
1147          for(p = 0; p<count; p++)
1148             if(!fstrcmp(paths[p], path))
1149                break;
1150
1151          if(p == count)
1152          {
1153             char * start;
1154             if(count) 
1155             {
1156                strcat(newPath, ";");
1157                start = oldPath + strlen(paths[count-1])+1;
1158             }
1159             else
1160                start = oldPath;
1161             
1162             strcpy(start, path);
1163             *(start + strlen(path)) = '\0';
1164             paths[count++] = start;
1165
1166             strcat(newPath, path);
1167          }
1168       }
1169    }
1170 #endif
1171 }
1172
1173 void AssociateExtension(char * extension, char * description, char *name, char * action, char * path)
1174 {
1175    HKEY key;
1176    uint status, size;
1177    char keyName[1024];
1178
1179    RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1180    RegSetValueEx(key, null, 0, REG_SZ, name, strlen(name)+1);
1181    RegCloseKey(key);
1182
1183    RegCreateKeyEx(HKEY_CLASSES_ROOT, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1184    RegSetValueEx(key, null, 0, REG_SZ, description, strlen(description)+1);
1185    RegCloseKey(key);
1186
1187    sprintf(keyName, "%s\\shell", extension);
1188    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1189    RegSetValueEx(key, null, 0, REG_SZ, action, strlen(action)+1);
1190    RegCloseKey(key);
1191
1192    sprintf(keyName, "%s\\shell\\%s", name, action);
1193    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1194    RegCloseKey(key);
1195
1196    sprintf(keyName, "%s\\shell\\%s\\command", name, action);
1197    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1198    
1199    sprintf(keyName, path);
1200    strcat(keyName, " \"%L\"");
1201    {
1202       uint16 wKeyName[2048];
1203       UTF8toUTF16Buffer(keyName, wKeyName, sizeof(wKeyName) / sizeof(uint16));
1204       RegSetValueExW(key, null, 0, REG_SZ, (byte *)wKeyName, (wcslen(wKeyName) + 1)*sizeof(uint16));
1205    }
1206    RegCloseKey(key);
1207 }
1208
1209 class InstallThread : Thread
1210 {
1211    unsigned int Main()
1212    {
1213       ComponentID c;
1214       ((GuiApplication)__thisModule).Lock();
1215       installProgress.progressBar.range = totalSize;
1216       for(c = 0; components[c].name && !abortInstall; c++)
1217          components[c].Install(installDir);
1218       if(abortInstall)
1219       {
1220          installProgress.progressBar.range = 0;
1221          installProgress.finish.Destroy(0);
1222          installProgress.cancel.text = $"Close";
1223          installProgress.cancel.isDefault = true;
1224          installProgress.cancel.disabled = false;
1225          installProgress.cancel.NotifyClicked = Window::ButtonCloseDialog;
1226          installProgress.installing.text = $"Installation Cancelled.";
1227          installProgress.title.text = $"Installation Cancelled";
1228          installProgress.titleInfo.contents = $"The installation was not completed.";
1229       }
1230       else
1231       {
1232          CoreSDKID c;
1233          char idePath[MAX_LOCATION];
1234          char userProfile[MAX_LOCATION];
1235
1236          // Configure IDE
1237          GlobalSettings settings
1238          {
1239             
1240          };
1241          installProgress.installing.text = $"Configuring ECERE IDE...";
1242          ((GuiApplication)__thisModule).Unlock();
1243          ((GuiApplication)__thisModule).SignalEvent();
1244
1245          settings.Load();
1246          for(c = 0; coreSDK[c].name; c++)
1247          {
1248             char path[MAX_LOCATION];
1249             NamedItem item;
1250             coreSDK[c].GetFullPath(path);
1251             if(c != ide && c != runtime && c != eda && c != ec)
1252                PathCat(path, "bin");
1253             if(c == ide)
1254             {
1255                coreSDK[c].GetFullPath(idePath);
1256                PathCat(idePath, "IDE.exe");
1257             }
1258
1259             // TODO: Update This!
1260             /*
1261             for(item = settings.systemDirs[executables].first; item; item = item.next)
1262                if(!fstrcmp(item.name, path))
1263                   break;
1264             if(!item)
1265             {
1266                settings.systemDirs[executables].Add(NamedItem { name = CopyString(path); });
1267             }
1268
1269             if(c == runtime)
1270             {
1271                for(item = settings.systemDirs[libraries].first; item; item = item.next)
1272                   if(!fstrcmp(item.name, path))
1273                      break;
1274                if(!item)
1275                {
1276                   settings.systemDirs[libraries].Add(NamedItem { name = CopyString(path); });
1277                }
1278             }
1279             */
1280          }
1281 #ifndef NOMINGW
1282          /*
1283          for(c = 0; additional[c].name; c++)
1284          {
1285             char path[MAX_LOCATION];
1286             NamedItem item;
1287             additional[c].GetFullPath(path);
1288             PathCat(path, "bin");
1289             for(item = settings.systemDirs[executables].first; item; item = item.next)
1290                if(!fstrcmp(item.name, path))
1291                   break;
1292             if(!item)
1293             {
1294                settings.systemDirs[executables].Add(NamedItem { name = CopyString(path); });
1295             }
1296          }
1297          */
1298 #endif
1299          
1300          {
1301             char path[MAX_LOCATION] = "";
1302             if(components[samples].selected)
1303                components[samples].GetFullPath(path);
1304             else
1305                components[coreSDK].GetFullPath(path);
1306             /* TODO: Update This!
1307             if(!settings.ideProjectFileDialogLocation)
1308                settings.ideProjectFileDialogLocation = path;
1309             if(!settings.ideFileDialogLocation)
1310                settings.ideFileDialogLocation = path;
1311             */
1312          }
1313
1314          settings.Save();
1315          delete settings;
1316
1317          // Set up Uninstaller
1318          ((GuiApplication)__thisModule).Lock();
1319          installProgress.installing.text = $"Registering uninstaller...";
1320          ((GuiApplication)__thisModule).Unlock();
1321          ((GuiApplication)__thisModule).SignalEvent();
1322
1323          {
1324             HKEY key;
1325             uint status, size;
1326             char * displayName = "ECERE SDK 0.44";
1327             char uninstaller[MAX_LOCATION];
1328             bool nomodify = true;
1329
1330             strcpy(uninstaller, installDir);
1331             PathCat(uninstaller, "uninstall_ecere.exe");
1332
1333             RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ECERE SDK", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1334
1335             RegSetValueEx(key, "DisplayName", 0, REG_SZ, displayName, strlen(displayName)+1);
1336             RegSetValueEx(key, "UninstallString", 0, REG_SZ, uninstaller, strlen(uninstaller)+1);
1337             RegSetValueEx(key, "DisplayIcon", 0, REG_SZ, idePath, strlen(idePath)+1);
1338             //RegSetValueEx(key, "NoModify", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1339             //RegSetValueEx(key, "NoRepair", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1340             RegCloseKey(key);
1341          }
1342
1343          // Add paths
1344          if(pathOptions[PathOptions::AddECEREPaths].selected 
1345 #ifndef NOMINGW
1346             || pathOptions[PathOptions::AddMinGWPaths].selected
1347 #endif
1348             )
1349          {
1350             HKEY key;
1351             uint status, size;
1352             char path[2048] = "";
1353             uint16 wPath[2048];
1354
1355             ((GuiApplication)__thisModule).Lock();
1356             installProgress.installing.text = "Registering paths...";
1357             ((GuiApplication)__thisModule).Unlock();
1358             ((GuiApplication)__thisModule).SignalEvent();
1359                         
1360             // if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Environment", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1361             
1362             RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1363             // RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1364             if(status == REG_OPENED_EXISTING_KEY)
1365             {
1366                size = sizeof(wPath);
1367                RegQueryValueExW(key, L"path", null, null, (byte *)wPath, &size);
1368                UTF16toUTF8Buffer(wPath, path, sizeof(path));
1369             }
1370             ModifyPath(path);
1371             UTF8toUTF16Buffer(path, wPath, sizeof(wPath) / sizeof(uint16));
1372             RegSetValueExW(key, L"path", 0, REG_EXPAND_SZ, (byte *)wPath, (wcslen(wPath)+1) * 2);
1373             RegCloseKey(key);
1374
1375             SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, 0, (int)"Environment", SMTO_NORMAL, 1000, NULL);
1376          }
1377
1378          // Install Program Group Icons
1379          // userProfile = getenv("USERPROFILE");
1380          GetEnvironment("USERPROFILE", userProfile, sizeof(userProfile));
1381
1382          if(options[IconOptions::StartMenuIcon].selected)
1383          {
1384             char destPath[MAX_LOCATION];
1385             char startMenuPath[MAX_LOCATION] = "";
1386             HKEY key;
1387
1388             ((GuiApplication)__thisModule).Lock();
1389             installProgress.installing.text = $"Installing Start Menu Icons...";
1390             ((GuiApplication)__thisModule).Unlock();
1391             ((GuiApplication)__thisModule).SignalEvent();
1392
1393             strcpy(destPath, userProfile);
1394
1395             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1396             {
1397                uint16 wStartMenuPath[2048];
1398                uint size = sizeof(wStartMenuPath);
1399                // RegQueryValueEx(key, "Start Menu", null, null, startMenuPath, &size);
1400                RegQueryValueExW(key, L"Programs", null, null, (byte *)wStartMenuPath, &size);
1401                UTF16toUTF8Buffer(wStartMenuPath, startMenuPath, sizeof(startMenuPath));
1402                RegCloseKey(key);
1403             }
1404             if(!startMenuPath[0] && userProfile && userProfile[0])
1405             {
1406                strcpy(startMenuPath, userProfile);
1407                PathCat(startMenuPath, "Start Menu\\Programs");
1408             }
1409
1410             if(startMenuPath[0])
1411             {
1412                strcpy(destPath, startMenuPath);
1413                PathCat(destPath, "ECERE SDK");
1414                MakeDir(destPath);
1415
1416                strcpy(destPath, startMenuPath);
1417                PathCat(destPath, "ECERE SDK\\ECERE IDE.lnk");
1418                CreateLink(idePath, destPath, null); //"ECERE IDE");
1419                if(components[samples].selected)
1420                {
1421                   char samplesPath[MAX_LOCATION] = "";
1422                   components[samples].GetFullPath(samplesPath);
1423
1424                   strcpy(destPath, startMenuPath);
1425                   PathCat(destPath, "ECERE SDK\\Sample Projects.lnk");
1426                   CreateLink(samplesPath, destPath, null);//"Sample Projects");
1427                }
1428                if(components[documentation].selected && documentation[ecereBook].selected)
1429                {
1430                   char docPath[MAX_LOCATION] = "";
1431                   documentation[ecereBook].GetFullPath(docPath);
1432                   PathCat(docPath, "Ecere Tao of Programming [work in progress].pdf");
1433
1434                   strcpy(destPath, startMenuPath);
1435                   PathCat(destPath, "ECERE SDK\\The Ecere Tao of Programming.lnk");
1436                   CreateLink(docPath, destPath, null);
1437                }
1438             }
1439          }
1440
1441          // Install Desktop Icon
1442          if(options[IconOptions::DesktopIcon].selected)
1443          {
1444             HKEY key;
1445             char desktopPath[MAX_LOCATION];
1446
1447             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1448             {
1449                uint16 wDesktopPath[MAX_LOCATION];
1450                uint size = sizeof(wDesktopPath);
1451                RegQueryValueExW(key, L"Desktop", null, null, (byte *)wDesktopPath, &size);
1452                UTF16toUTF8Buffer(wDesktopPath, desktopPath, sizeof(desktopPath));
1453                RegCloseKey(key);
1454             }
1455             if(!desktopPath[0] && userProfile && userProfile[0])
1456             {
1457                strcpy(desktopPath, userProfile);
1458                PathCat(desktopPath, "Desktop");
1459             }
1460
1461             if(desktopPath[0])
1462             {
1463                PathCat(desktopPath, "ECERE IDE.lnk");
1464
1465                ((GuiApplication)__thisModule).Lock();
1466                installProgress.installing.text = $"Installing Desktop Icon...";
1467                ((GuiApplication)__thisModule).Unlock();
1468                ((GuiApplication)__thisModule).SignalEvent();
1469
1470                CreateLink(idePath, desktopPath, null);//"ECERE IDE");
1471             }
1472          }
1473
1474          // Install QuickLaunch Icon
1475          if(options[IconOptions::QuickLaunchIcon].selected)
1476          {
1477             char appData[MAX_LOCATION]; // = getenv("APPDATA");
1478             GetEnvironment("APPDATA", appData, sizeof(appData));
1479
1480             if(appData && appData[0])
1481             {
1482                char destPath[MAX_LOCATION];
1483
1484                ((GuiApplication)__thisModule).Lock();
1485                installProgress.installing.text = $"Installing Quicklaunch Icon...";
1486                ((GuiApplication)__thisModule).Unlock();
1487                ((GuiApplication)__thisModule).SignalEvent();
1488
1489                strcpy(destPath, appData);
1490                PathCat(destPath, "Microsoft\\Internet Explorer\\Quick Launch\\ECERE IDE.lnk");
1491
1492                CreateLink(idePath, destPath, null);//"ECERE IDE");
1493             }
1494          }
1495
1496          // Register File Types
1497          if(associateOptions[AssociateOptions::AssociateEPJ].selected ||
1498             associateOptions[AssociateOptions::AssociateEC].selected ||
1499             associateOptions[AssociateOptions::AssociateC].selected ||
1500             associateOptions[AssociateOptions::AssociateCPP].selected ||
1501             associateOptions[AssociateOptions::AssociateTXT].selected ||
1502             associateOptions[AssociateOptions::Associate3DS].selected ||
1503             associateOptions[AssociateOptions::AssociateIMG].selected)
1504          {
1505             ((GuiApplication)__thisModule).Lock();
1506             installProgress.installing.text = $"Resgistering File Types...";
1507             ((GuiApplication)__thisModule).Unlock();
1508             ((GuiApplication)__thisModule).SignalEvent();
1509             
1510             if(associateOptions[AssociateOptions::AssociateEPJ].selected)
1511             {
1512                AssociateExtension(".epj", "ECERE IDE Project", "epj_file", "Open", idePath);
1513             }
1514             if(associateOptions[AssociateOptions::AssociateEC].selected)
1515             {
1516                AssociateExtension(".ec", "eC Source File", "ec_file", "Open", idePath);
1517                AssociateExtension(".eh", "eC Header File", "eh_file", "Open", idePath);
1518             }
1519             if(associateOptions[AssociateOptions::AssociateC].selected)
1520             {
1521                AssociateExtension(".c", "C Source File", "c_file", "Open", idePath);
1522                AssociateExtension(".h", "C Header File", "h_file", "Open", idePath);
1523             }
1524             if(associateOptions[AssociateOptions::AssociateCPP].selected)
1525             {
1526                AssociateExtension(".cpp", "C++ Source File", "cpp_file", "Open", idePath);
1527                AssociateExtension(".cc", "C++ Source File", "cpp_file", "Open", idePath);
1528                AssociateExtension(".cxx", "C++ Source File", "cpp_file", "Open", idePath);
1529                AssociateExtension(".chh", "C++ Header File", "chh_file", "Open", idePath);
1530                AssociateExtension(".hh", "C++ Header File", "chh_file", "Open", idePath);
1531                AssociateExtension(".hxx", "C++ Header File", "chh_file", "Open", idePath);
1532             }
1533             if(associateOptions[AssociateOptions::AssociateTXT].selected)
1534             {
1535                AssociateExtension(".txt", "Text File", "txt_file", "Open", idePath);
1536             }
1537             if(associateOptions[AssociateOptions::Associate3DS].selected)
1538             {
1539                AssociateExtension(".3ds", "3D Studio Model", "3ds_file", "View", idePath);
1540             }
1541             if(associateOptions[AssociateOptions::AssociateIMG].selected)
1542             {
1543                AssociateExtension(".bmp", "BMP Image", "bmp_file", "View", idePath);
1544                AssociateExtension(".pcx", "PCX Image", "pcx_file", "View", idePath);
1545                AssociateExtension(".png", "PNG Image", "png_file", "View", idePath);
1546                AssociateExtension(".jpg", "JPEG Image", "jpg_file", "View", idePath);
1547                AssociateExtension(".jpeg", "JPEG Image", "jpg_file", "View", idePath);
1548                AssociateExtension(".gif", "GIF Image", "gif_file", "View", idePath);
1549             }
1550          }
1551          ((GuiApplication)__thisModule).Lock();
1552
1553          installProgress.cancel.Destroy(0);
1554          installProgress.finish.text = $"Finish";
1555          installProgress.finish.disabled = false;
1556          installProgress.finish.Activate();
1557          installProgress.installing.text = $"Installation Complete.";
1558          installProgress.title.text = $"Installation Complete";
1559          installProgress.titleInfo.contents = $"Thank you for using the Ecere SDK.";
1560       }
1561       ((GuiApplication)__thisModule).Unlock();
1562       return 0;
1563    }
1564 }