8aac572b2d8de7ee6c011d9db2662fe89966eda2
[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 - v0.44 \"Ryōan-ji\"";
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    Picture back { image = BitmapResource { ":ryoanji.png" }, parent = this, position = { 0, 0 } };
552    FileDialog fileDialog
553    {
554       master = this, type = selectDir,
555       text = $"Select a new location"
556    };
557    Button browse
558    {
559       master = this, autoCreate = false, inactive = true, /*hotKey = F2,*/ text = "...";
560       
561       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
562       {
563          DataRow row = componentsBox.currentRow;
564          Component * component = ((CheckItem *)row.GetData(componentField))->data;
565          component->GetFullPath(fileDialog.filePath);
566          StripLastDirectory(fileDialog.filePath, fileDialog.currentDirectory);
567
568          if(fileDialog.Modal() == ok)
569          {
570             componentsBox.StopEditing(false);
571             row.SetData(locationField, fileDialog.filePath);
572             componentsBox.NotifyChanged(this, componentsBox, row);
573             componentsBox.currentField = componentsBox.currentField;
574          }
575          return true;
576       }
577    };
578    CheckListBox componentsBox
579    {
580       this, size = { 460, 112 }, position = { 160, 160 }, hasHeader = true;
581       alwaysEdit = true;
582       opacity = 0;
583
584
585       bool NotifyChanged(ListBox listBox, DataRow row)
586       {
587          Component * component = ((CheckItem *)listBox.GetData(componentField))->data;
588          char path[MAX_LOCATION], relative[MAX_LOCATION] = "", ** newPath;
589          char fullPath[MAX_LOCATION];
590
591          component->parent->GetFullPath(path);
592          strcpy(fullPath, path);
593
594          newPath = (char **)row.GetData(locationField);
595          if(newPath && *newPath)
596          {
597             PathCat(fullPath, *newPath);
598             MakePathRelative(fullPath, path, relative);
599          }
600          listBox.SetData(locationField, relative);
601          strcpy(component->installPath, relative);
602
603          //SetAvailableSpace(component, path);
604          {
605             ComponentID c;
606             install.disabled = false;
607             for(c = 0; components[c].name; c++)
608             {
609                SetAvailableSpace(components[c], installDir);
610             }
611          }
612          return true;
613       }
614
615       bool NotifyEditDone(ListBox listBox, DataRow row)
616       {
617          browse.Destroy(0);
618          return true;
619       }
620
621       bool NotifyEdited(ListBox listBox, DataRow row)
622       {
623          browse.parent = listBox;
624          browse.position = { componentField.width + locationField.width + 18, (listBox.currentIndex+1) * listBox.rowHeight - 2 };
625          browse.size = { 30, listBox.rowHeight + 3 };
626
627          browse.Create();
628          return true;
629       }
630
631       void NotifyChecked(CheckListBox listBox, DataRow row)
632       {
633          Component * component = ((CheckItem *)row.GetData(componentField))->data;
634          int c;
635          Button checkBox = (Button)row.tag;
636          component->selected = checkBox.checked;
637
638          if(!component->parent) totalSize -= component->requiredSize;
639          component->requiredSize = 0;
640          if(component->selected)
641          {
642             component->requiredSize += component->size; 
643             if(component->subComponents)
644                for(c = 0; component->subComponents[c].name; c++)
645                   component->requiredSize += component->subComponents[c].requiredSize;
646          }
647          if(component->requiredSize)
648          {
649             uint requiredSize = component->requiredSize;
650             row.SetData(reqField, requiredSize);
651          }
652          else
653             row.UnsetData(reqField);
654          if(!component->parent) 
655          {
656             totalSize += component->requiredSize;
657             {
658                char sizeString[100];
659                PrintSize(sizeString, totalSize, 2);
660                totalSpaceValue.text = sizeString;
661             }
662          }
663       }
664    };
665    Label agreementLbl { parent = this, text = $"By installing the Ecere SDK, you agree to the                                         .", font = { "Tahoma", 8.25f }, anchor = Anchor { left = 24, top = 424 } };
666    Button licenseButton
667    {
668       this, inactive = true, offset = false, bevel = false, foreground = blue, font = { "Tahoma", 8.25f, underline = true, bold = true },
669       // text = $"terms and conditions", anchor = Anchor { left = 241, top = 421 };
670       text = $"terms and conditions", anchor = Anchor { left = 237, top = 421 };
671       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
672
673       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
674       {
675          LicenseBox { master = this, sourceFile = ":ecere-sdk/doc/LICENSE" }.Modal();
676          return true;
677       }
678    };
679    CheckListBox optionsBox
680    {
681       this, size = { 460, 94 }, position = { 160, 284 };
682       opacity = 0;
683
684       void NotifyChecked(CheckListBox listBox, DataRow row)
685       {
686          CheckItem * item = row.GetData(optionField);
687          InstallOption * option = item->data;
688          int c;
689          Button checkBox = (Button)row.tag;
690          option->selected = checkBox.checked;
691       }
692    };
693    Button install
694    {
695       parent = this, text = $"Install", isDefault = true, size = { 75, 23 }, position = { 432, 416 };
696
697       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
698       {
699          installProgress.Create();
700          Destroy(0);
701          // installProgress.thread.Main();
702          installProgress.thread.Create();
703          return true;
704       }
705    };
706    Button button3 { parent = this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 }, NotifyClicked = ButtonCloseDialog };
707    Label label1 { labeledWindow = destBox, tabCycle = true, isGroupBox = true, parent = this, inactive = false, size = Size { 458, 50 }, anchor = Anchor { left = 160, top = 96 } };
708    EditBox destBox
709    {
710       parent = label1, master = this, text = $" Destination Folder", size = Size { 336, 19 }, anchor = Anchor { left = 12, top = 20 };
711
712       bool NotifyModified(EditBox editBox)
713       {
714          ComponentID c;
715          strcpy(installDir, destBox.contents);
716          install.disabled = false;
717          for(c = 0; components[c].name; c++)
718          {
719             SetAvailableSpace(components[c], installDir);
720          }
721          return true;
722       }
723    };
724    Button button1
725    {
726       label1, this, $"Browse", altB, size = { 83, 24 }, position = { 360, 16 };
727
728       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
729       {
730          strcpy(fileDialog.filePath, installDir);
731          StripLastDirectory(installDir, fileDialog.currentDirectory);
732          if(fileDialog.Modal())
733          {
734             strcpy(installDir, fileDialog.filePath);
735             destBox.contents = installDir;
736             // TOCHECK: Should setting contents call NotifyModified?
737             destBox.NotifyModified(destBox.master, destBox);
738          }
739          return true;
740       }
741    };
742    EditBox label5
743    {
744       this, multiLine = true,
745       opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 96 }, noSelect = true,
746       contents = $"Select the default root\n"
747          "folder where to install\n"
748          "all components:"
749    };
750    EditBox label6
751    {
752       this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 132 }, position = { 14, 152 }, noSelect = true,
753       multiLine = true,
754       contents = $"Select the optional\n"
755          "components you wish\n"
756          "to install:\n\n"
757          "You may customize the\n"
758          "install location for each\n"
759          "of them, or use the default\n"
760          "based on the main\n"
761          "destination folder."
762    };
763    EditBox label7
764    {
765       this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 280 }, noSelect = true, 
766       multiLine = true,
767       contents = $"Select icons to install, file\n"
768       "associations, and system\n"
769       "environment modifications:"
770    };
771    Label totalSpaceLabel
772    {
773       this, position = { 18, 352 }, text = $"Space Required: "
774    };
775    Label totalSpaceValue
776    {
777       this, position = { 100, 352 }, text = "0 mb"
778    };
779    EditBox editBox1
780    {
781       inactive = true, noSelect = true,
782       multiLine = true, parent = label3, text = "editBox1", opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
783       contents = $"Choose in which folder to install the ECERE SDK, which features\n"
784          "of the SDK to install, as well as where to install program icons."
785    };
786    Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
787    Picture picture1 { image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, text = "picture1", anchor = Anchor { left = 16, top = 4 } };
788    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 } };
789    DataField componentField { "CheckItem", width = 140, header = $"Component" };
790    DataField locationField { "char *", width = 108, header = $"Destination Folder", editable = true };
791    DataField reqField { "FileSize", width = 80, header = $"Req. Space", alignment = right };
792    DataField avField { "FileSize64", width = 80, header = $"Avail. Space", alignment = right };
793    DataField optionField { "CheckItem" };
794
795    void SetAvailableSpace(Component component, char * parentPath)
796    {
797       char path[MAX_LOCATION];
798       int c;
799       FileSize64 size = 0;
800
801       strcpy(path, parentPath);
802       PathCat(path, component.installPath);
803
804       if(component.subComponents)
805          for(c = 0; component.subComponents[c].name; c++)
806             SetAvailableSpace(component.subComponents[c], path);
807
808       while(!FileExists(path) && path[0])
809          StripLastDirectory(path, path);
810
811       if(path[0])
812          GetFreeSpace(path, &size);
813       component.row.SetData(avField, size);
814       if(!size) install.disabled = true;
815    }
816
817    FileSize ComputeSize(char * path)
818    {
819       FileSize size = 0;
820       FileAttribs attribs = FileExists(path);
821       if(attribs.isDirectory)
822       {
823          FileListing listing { path };
824          while(listing.Find())
825          {
826             if(listing.stats.attribs.isDirectory)
827                size += ComputeSize(listing.path);
828             else
829                size += listing.stats.size;
830          }
831       }
832       else
833          FileGetSize(path, &size);
834       return size;
835    }
836
837    void AddComponent(Component component, Component parent, char * parentPath)
838    {
839       DataRow row = component.row = componentsBox.AddRow((parent != null) ? parent.row : null);
840       Button checkBox = (Button) row.tag;
841       FileSize size = 0;
842       FileSize64 avSize = 0;
843       char path[MAX_LOCATION];
844       strcpy(path, parentPath);
845       if(component.defInstallPath)
846          PathCat(path, component.defInstallPath);
847       component.parent = parent;
848          
849       row.SetData(null, CheckItem { component.name, component } );
850
851       if(component.defInstallPath)
852       {
853          strcpy(component.installPath, component.defInstallPath);
854          ChangeCh(component.installPath, '/', DIR_SEP);
855          row.SetData(locationField, component.installPath);
856       }
857
858       if(component.mandatory) checkBox.disabled = true;
859       if(!component.selected) componentsBox.ToggleCheck(row);
860       if(component.dataPath)
861       {
862          char path[MAX_LOCATION];
863          strcpy(path, ":");
864          PathCat(path, component.dataPath);
865          component.size = ComputeSize(path);
866       }
867       if(component.subComponents)
868       {
869          int c;
870          for(c = 0; component.subComponents[c].name; c++)
871          {
872             AddComponent(component.subComponents[c], component, path);
873             size += component.subComponents[c].requiredSize;
874          }
875       }
876
877       component.requiredSize = component.selected ? (size + component.size) : 0;
878       if(component.requiredSize)
879          row.SetData(reqField, component.requiredSize);
880
881       while(!FileExists(path) && path[0])
882          StripLastDirectory(path, path);
883       
884       if(path[0])
885          GetFreeSpace(path, &avSize);
886       else
887          avSize = 0;
888       row.SetData(avField, avSize);
889       row.collapsed = true;
890    }
891
892    void AddOption(InstallOption option, InstallOption parent)
893    {
894       DataRow row = option.row = optionsBox.AddRow((parent != null) ? parent.row : null);
895       row.SetData(null, CheckItem { option.name, option } );
896       if(!option.selected)
897          optionsBox.ToggleCheck(row);
898       if(option.subOptions)
899       {
900          int c;
901          for(c = 0; option.subOptions[c].name; c++)
902          {
903             AddOption(option.subOptions[c], option);
904          }
905       }
906       row.collapsed = true;
907    }
908
909    Installer()
910    {
911       int c;
912       char programFilesDir[MAX_LOCATION];
913       char appData[MAX_LOCATION]; // = getenv("APPDATA");
914       char homeDrive[MAX_LOCATION]; //= getenv("HOMEDRIVE");
915       char winDir[MAX_LOCATION]; //= getenv("windir");
916
917       GetEnvironment("APPDATA", appData, sizeof(appData));
918       GetEnvironment("HOMEDRIVE", homeDrive, sizeof(homeDrive));
919       GetEnvironment("windir", winDir, sizeof(winDir));
920       
921       componentsBox.AddField(componentField);
922       componentsBox.AddField(locationField);
923       componentsBox.AddField(reqField);
924       componentsBox.AddField(avField);
925
926       optionsBox.AddField(optionField);
927
928       if(GetEnvironment("ProgramFiles", programFilesDir, MAX_LOCATION))
929       {
930          strcpy(installDir, programFilesDir);
931          PathCat(installDir, "ECERE SDK");
932       }
933       else if(homeDrive && homeDrive[0])
934       {
935          strcpy(installDir, homeDrive);
936          PathCat(installDir, "ECERE SDK");
937       }
938       else if(winDir && winDir[0])
939       {
940          strcpy(installDir, winDir);
941          PathCat(installDir, "..\\ECERE SDK");
942       }
943       else
944          strcpy(installDir, "C:\\ECERE SDK");
945       
946       if(appData && appData[0])
947       {
948          static char defSamplesPath[MAX_LOCATION];
949          static char defExtrasPath[MAX_LOCATION];
950          strcpy(defSamplesPath, appData);
951          PathCat(defSamplesPath, "ECERE SDK\\Samples");
952          components[samples].defInstallPath = defSamplesPath;
953
954          strcpy(defExtrasPath, appData);
955          PathCat(defExtrasPath, "ECERE SDK\\extras");
956          coreSDK[extras].defInstallPath = defExtrasPath;
957       }
958          
959       destBox.contents = installDir;
960
961       {
962          ComponentID c;
963          for(c = 0; components[c].name; c++)
964             AddComponent(components[c], null, installDir);
965       }
966
967       // Compute size apart because ToggleCheck will change it
968       totalSize = 0;
969       {
970          ComponentID c;
971          for(c = 0; components[c].name; c++)
972             totalSize += components[c].requiredSize;
973       }
974       {
975          char sizeString[100];
976          PrintSize(sizeString, totalSize, 2);
977          totalSpaceValue.text = sizeString;
978       }
979       for(c = 0; options[c].name; c++)
980          AddOption(options[c], null);
981    }
982
983    bool OnCreate()
984    {
985       destBox.Activate();
986       return true;
987    }
988
989    void OnDrawOverChildren(Surface surface)
990    {
991       int tw = label2.size.w;
992       surface.SetForeground(Color { 128, 128, 128 });
993       surface.HLine(label2.position.x + tw + 6, 620, 400);
994       surface.SetForeground(white);
995       surface.HLine(label2.position.x + tw + 6, 621, 401);
996       surface.PutPixel(621, 400);
997    }
998
999    Label label3
1000    {
1001       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
1002    };
1003 }
1004
1005 class InstallProgress : Window
1006 {
1007    text = $"Ecere Software Development Kit Setup - v0.44 \"Ryōan-ji\"";
1008    background = activeBorder;
1009    borderStyle = fixed;
1010    hasMinimize = true;
1011    hasClose = true;
1012    tabCycle = true;
1013    size = Size { 640, 480 };
1014    icon = { ":icon.png" };
1015
1016    Picture back { image = BitmapResource { ":ryoanji-progress.png" }, parent = this, position = { 0, 0 } };
1017    Label installing { this, position = { 32, 160 } };
1018    ProgressBar progressBar { parent = this, size = Size { 588, 24 }, anchor = Anchor { left = 24, top = 184 } };
1019    Button finish
1020    {
1021       parent = this, text = $"Install", disabled = true, isDefault = true, size = Size { 75, 23 }, anchor = Anchor { left = 432, top = 416 };
1022
1023       NotifyClicked = ButtonCloseDialog
1024    };
1025    Button cancel
1026    {
1027       this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 };
1028
1029       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1030       {
1031          abortInstall = true;
1032          return true;
1033       }
1034    };
1035    EditBox titleInfo
1036    {
1037       inactive = true, noSelect = true,
1038       multiLine = true, parent = label3, opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
1039       contents = $"Please wait while the Ecere Software Development Kit is being installed."
1040    };
1041    Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
1042    Picture picture1 { image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, anchor = Anchor { left = 16, top = 4 } };
1043    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 } };
1044
1045    void OnDrawOverChildren(Surface surface)
1046    {
1047       int tw = label2.size.w;
1048       surface.SetForeground(Color { 128, 128, 128 });
1049       surface.HLine(label2.position.x + tw + 6, 620, 400);
1050       surface.SetForeground(white);
1051       surface.HLine(label2.position.x + tw + 6, 621, 401);
1052       surface.PutPixel(621, 400);
1053    }
1054
1055    Label label3
1056    {
1057       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
1058    };
1059    InstallThread thread
1060    {
1061    };
1062 }
1063
1064 Installer installer {};
1065 InstallProgress installProgress { autoCreate = false };
1066
1067 void ModifyPath(char * newPath)
1068 {
1069    char * paths[100];
1070    int p, count;
1071    char oldPath[4096];
1072    CoreSDKID c;
1073
1074    strcpy(oldPath, newPath);
1075    count = TokenizeWith(oldPath, sizeof(paths) / sizeof(char *), paths, ";", false);
1076
1077    for(c = 0; coreSDK[c].name; c++)
1078    {
1079       char path[MAX_LOCATION];
1080       coreSDK[c].GetFullPath(path);
1081       if(c != ide && c != runtime && c != eda && c != ec && c !=extras && c != vanilla)
1082       {
1083 #ifndef NOMINGW
1084          if(!pathOptions[PathOptions::AddMinGWPaths].selected)
1085 #endif
1086          continue;
1087          PathCat(path, "bin");
1088       }
1089       else if(!pathOptions[PathOptions::AddECEREPaths].selected) continue;
1090
1091       for(p = 0; p<count; p++)
1092          if(!fstrcmp(paths[p], path))
1093             break;
1094       if(p == count)
1095       {
1096          char * start;
1097          if(count) 
1098          {
1099             strcat(newPath, ";");
1100             start = oldPath + strlen(paths[count-1])+1;
1101          }
1102          else
1103             start = oldPath;
1104          
1105          strcpy(start, path);
1106          *(start + strlen(path)) = '\0';
1107          paths[count++] = start;
1108
1109          strcat(newPath, path);
1110       }
1111    }
1112 #ifndef NOMINGW
1113    if(pathOptions[PathOptions::AddMinGWPaths].selected)
1114    {
1115       int c;
1116       for(c = 0; additional[c].name; c++)
1117       {
1118          char path[MAX_LOCATION];
1119          NamedItem item;
1120          additional[c].GetFullPath(path);
1121          PathCat(path, "bin");
1122          for(p = 0; p<count; p++)
1123             if(!fstrcmp(paths[p], path))
1124                break;
1125
1126          if(p == count)
1127          {
1128             char * start;
1129             if(count) 
1130             {
1131                strcat(newPath, ";");
1132                start = oldPath + strlen(paths[count-1])+1;
1133             }
1134             else
1135                start = oldPath;
1136             
1137             strcpy(start, path);
1138             *(start + strlen(path)) = '\0';
1139             paths[count++] = start;
1140
1141             strcat(newPath, path);
1142          }
1143       }
1144    }
1145 #endif
1146 }
1147
1148 void AssociateExtension(char * extension, char * description, char *name, char * action, char * path)
1149 {
1150    HKEY key;
1151    uint status, size;
1152    char keyName[1024];
1153
1154    RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1155    RegSetValueEx(key, null, 0, REG_SZ, name, strlen(name)+1);
1156    RegCloseKey(key);
1157
1158    RegCreateKeyEx(HKEY_CLASSES_ROOT, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1159    RegSetValueEx(key, null, 0, REG_SZ, description, strlen(description)+1);
1160    RegCloseKey(key);
1161
1162    sprintf(keyName, "%s\\shell", extension);
1163    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1164    RegSetValueEx(key, null, 0, REG_SZ, action, strlen(action)+1);
1165    RegCloseKey(key);
1166
1167    sprintf(keyName, "%s\\shell\\%s", name, action);
1168    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1169    RegCloseKey(key);
1170
1171    sprintf(keyName, "%s\\shell\\%s\\command", name, action);
1172    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1173    
1174    sprintf(keyName, path);
1175    strcat(keyName, " \"%L\"");
1176    {
1177       uint16 wKeyName[2048];
1178       UTF8toUTF16Buffer(keyName, wKeyName, sizeof(wKeyName) / sizeof(uint16));
1179       RegSetValueExW(key, null, 0, REG_SZ, (byte *)wKeyName, (wcslen(wKeyName) + 1)*sizeof(uint16));
1180    }
1181    RegCloseKey(key);
1182 }
1183
1184 class InstallThread : Thread
1185 {
1186    unsigned int Main()
1187    {
1188       ComponentID c;
1189       ((GuiApplication)__thisModule).Lock();
1190       installProgress.progressBar.range = totalSize;
1191       for(c = 0; components[c].name && !abortInstall; c++)
1192          components[c].Install(installDir);
1193       if(abortInstall)
1194       {
1195          installProgress.progressBar.range = 0;
1196          installProgress.finish.Destroy(0);
1197          installProgress.cancel.text = $"Close";
1198          installProgress.cancel.isDefault = true;
1199          installProgress.cancel.disabled = false;
1200          installProgress.cancel.NotifyClicked = Window::ButtonCloseDialog;
1201          installProgress.installing.text = $"Installation Cancelled.";
1202          installProgress.title.text = $"Installation Cancelled";
1203          installProgress.titleInfo.contents = $"The installation was not completed.";
1204       }
1205       else
1206       {
1207          CoreSDKID c;
1208          char idePath[MAX_LOCATION];
1209          char userProfile[MAX_LOCATION];
1210
1211          // Configure IDE
1212          GlobalSettings settings
1213          {
1214             
1215          };
1216          installProgress.installing.text = $"Configuring ECERE IDE...";
1217          ((GuiApplication)__thisModule).Unlock();
1218          ((GuiApplication)__thisModule).SignalEvent();
1219
1220          settings.Load();
1221          for(c = 0; coreSDK[c].name; c++)
1222          {
1223             char path[MAX_LOCATION];
1224             NamedItem item;
1225             coreSDK[c].GetFullPath(path);
1226             if(c != ide && c != runtime && c != eda && c != ec)
1227                PathCat(path, "bin");
1228             if(c == ide)
1229             {
1230                coreSDK[c].GetFullPath(idePath);
1231                PathCat(idePath, "IDE.exe");
1232             }
1233
1234             // TODO: Update This!
1235             /*
1236             for(item = settings.systemDirs[executables].first; item; item = item.next)
1237                if(!fstrcmp(item.name, path))
1238                   break;
1239             if(!item)
1240             {
1241                settings.systemDirs[executables].Add(NamedItem { name = CopyString(path); });
1242             }
1243
1244             if(c == runtime)
1245             {
1246                for(item = settings.systemDirs[libraries].first; item; item = item.next)
1247                   if(!fstrcmp(item.name, path))
1248                      break;
1249                if(!item)
1250                {
1251                   settings.systemDirs[libraries].Add(NamedItem { name = CopyString(path); });
1252                }
1253             }
1254             */
1255          }
1256 #ifndef NOMINGW
1257          /*
1258          for(c = 0; additional[c].name; c++)
1259          {
1260             char path[MAX_LOCATION];
1261             NamedItem item;
1262             additional[c].GetFullPath(path);
1263             PathCat(path, "bin");
1264             for(item = settings.systemDirs[executables].first; item; item = item.next)
1265                if(!fstrcmp(item.name, path))
1266                   break;
1267             if(!item)
1268             {
1269                settings.systemDirs[executables].Add(NamedItem { name = CopyString(path); });
1270             }
1271          }
1272          */
1273 #endif
1274          
1275          {
1276             char path[MAX_LOCATION] = "";
1277             if(components[samples].selected)
1278                components[samples].GetFullPath(path);
1279             else
1280                components[coreSDK].GetFullPath(path);
1281             /* TODO: Update This!
1282             if(!settings.ideProjectFileDialogLocation)
1283                settings.ideProjectFileDialogLocation = path;
1284             if(!settings.ideFileDialogLocation)
1285                settings.ideFileDialogLocation = path;
1286             */
1287          }
1288
1289          settings.Save();
1290          delete settings;
1291
1292          // Set up Uninstaller
1293          ((GuiApplication)__thisModule).Lock();
1294          installProgress.installing.text = $"Registering uninstaller...";
1295          ((GuiApplication)__thisModule).Unlock();
1296          ((GuiApplication)__thisModule).SignalEvent();
1297
1298          {
1299             HKEY key;
1300             uint status, size;
1301             char * displayName = "ECERE SDK 0.44";
1302             char uninstaller[MAX_LOCATION];
1303             bool nomodify = true;
1304
1305             strcpy(uninstaller, installDir);
1306             PathCat(uninstaller, "uninstall_ecere.exe");
1307
1308             RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ECERE SDK", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1309
1310             RegSetValueEx(key, "DisplayName", 0, REG_SZ, displayName, strlen(displayName)+1);
1311             RegSetValueEx(key, "UninstallString", 0, REG_SZ, uninstaller, strlen(uninstaller)+1);
1312             RegSetValueEx(key, "DisplayIcon", 0, REG_SZ, idePath, strlen(idePath)+1);
1313             //RegSetValueEx(key, "NoModify", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1314             //RegSetValueEx(key, "NoRepair", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1315             RegCloseKey(key);
1316          }
1317
1318          // Add paths
1319          if(pathOptions[PathOptions::AddECEREPaths].selected 
1320 #ifndef NOMINGW
1321             || pathOptions[PathOptions::AddMinGWPaths].selected
1322 #endif
1323             )
1324          {
1325             HKEY key;
1326             uint status, size;
1327             char path[2048] = "";
1328             uint16 wPath[2048];
1329
1330             ((GuiApplication)__thisModule).Lock();
1331             installProgress.installing.text = "Registering paths...";
1332             ((GuiApplication)__thisModule).Unlock();
1333             ((GuiApplication)__thisModule).SignalEvent();
1334                         
1335             // if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Environment", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1336             
1337             RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1338             // RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1339             if(status == REG_OPENED_EXISTING_KEY)
1340             {
1341                size = sizeof(wPath);
1342                RegQueryValueExW(key, L"path", null, null, (byte *)wPath, &size);
1343                UTF16toUTF8Buffer(wPath, path, sizeof(path));
1344             }
1345             ModifyPath(path);
1346             UTF8toUTF16Buffer(path, wPath, sizeof(wPath) / sizeof(uint16));
1347             RegSetValueExW(key, L"path", 0, REG_EXPAND_SZ, (byte *)wPath, (wcslen(wPath)+1) * 2);
1348             RegCloseKey(key);
1349
1350             SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, 0, (int)"Environment", SMTO_NORMAL, 1000, NULL);
1351          }
1352
1353          // Install Program Group Icons
1354          // userProfile = getenv("USERPROFILE");
1355          GetEnvironment("USERPROFILE", userProfile, sizeof(userProfile));
1356
1357          if(options[IconOptions::StartMenuIcon].selected)
1358          {
1359             char destPath[MAX_LOCATION];
1360             char startMenuPath[MAX_LOCATION] = "";
1361             HKEY key;
1362
1363             ((GuiApplication)__thisModule).Lock();
1364             installProgress.installing.text = $"Installing Start Menu Icons...";
1365             ((GuiApplication)__thisModule).Unlock();
1366             ((GuiApplication)__thisModule).SignalEvent();
1367
1368             strcpy(destPath, userProfile);
1369
1370             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1371             {
1372                uint16 wStartMenuPath[2048];
1373                uint size = sizeof(wStartMenuPath);
1374                // RegQueryValueEx(key, "Start Menu", null, null, startMenuPath, &size);
1375                RegQueryValueExW(key, L"Programs", null, null, (byte *)wStartMenuPath, &size);
1376                UTF16toUTF8Buffer(wStartMenuPath, startMenuPath, sizeof(startMenuPath));
1377                RegCloseKey(key);
1378             }
1379             if(!startMenuPath[0] && userProfile && userProfile[0])
1380             {
1381                strcpy(startMenuPath, userProfile);
1382                PathCat(startMenuPath, "Start Menu\\Programs");
1383             }
1384
1385             if(startMenuPath[0])
1386             {
1387                strcpy(destPath, startMenuPath);
1388                PathCat(destPath, "ECERE SDK");
1389                MakeDir(destPath);
1390
1391                strcpy(destPath, startMenuPath);
1392                PathCat(destPath, "ECERE SDK\\ECERE IDE.lnk");
1393                CreateLink(idePath, destPath, null); //"ECERE IDE");
1394                if(components[samples].selected)
1395                {
1396                   char samplesPath[MAX_LOCATION] = "";
1397                   components[samples].GetFullPath(samplesPath);
1398
1399                   strcpy(destPath, startMenuPath);
1400                   PathCat(destPath, "ECERE SDK\\Sample Projects.lnk");
1401                   CreateLink(samplesPath, destPath, null);//"Sample Projects");
1402                }
1403                if(components[documentation].selected && documentation[ecereBook].selected)
1404                {
1405                   char docPath[MAX_LOCATION] = "";
1406                   documentation[ecereBook].GetFullPath(docPath);
1407                   PathCat(docPath, "Ecere Tao of Programming [work in progress].pdf");
1408
1409                   strcpy(destPath, startMenuPath);
1410                   PathCat(destPath, "ECERE SDK\\The Ecere Tao of Programming.lnk");
1411                   CreateLink(docPath, destPath, null);
1412                }
1413             }
1414          }
1415
1416          // Install Desktop Icon
1417          if(options[IconOptions::DesktopIcon].selected)
1418          {
1419             HKEY key;
1420             char desktopPath[MAX_LOCATION];
1421
1422             if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1423             {
1424                uint16 wDesktopPath[MAX_LOCATION];
1425                uint size = sizeof(wDesktopPath);
1426                RegQueryValueExW(key, L"Desktop", null, null, (byte *)wDesktopPath, &size);
1427                UTF16toUTF8Buffer(wDesktopPath, desktopPath, sizeof(desktopPath));
1428                RegCloseKey(key);
1429             }
1430             if(!desktopPath[0] && userProfile && userProfile[0])
1431             {
1432                strcpy(desktopPath, userProfile);
1433                PathCat(desktopPath, "Desktop");
1434             }
1435
1436             if(desktopPath[0])
1437             {
1438                PathCat(desktopPath, "ECERE IDE.lnk");
1439
1440                ((GuiApplication)__thisModule).Lock();
1441                installProgress.installing.text = $"Installing Desktop Icon...";
1442                ((GuiApplication)__thisModule).Unlock();
1443                ((GuiApplication)__thisModule).SignalEvent();
1444
1445                CreateLink(idePath, desktopPath, null);//"ECERE IDE");
1446             }
1447          }
1448
1449          // Install QuickLaunch Icon
1450          if(options[IconOptions::QuickLaunchIcon].selected)
1451          {
1452             char appData[MAX_LOCATION]; // = getenv("APPDATA");
1453             GetEnvironment("APPDATA", appData, sizeof(appData));
1454
1455             if(appData && appData[0])
1456             {
1457                char destPath[MAX_LOCATION];
1458
1459                ((GuiApplication)__thisModule).Lock();
1460                installProgress.installing.text = $"Installing Quicklaunch Icon...";
1461                ((GuiApplication)__thisModule).Unlock();
1462                ((GuiApplication)__thisModule).SignalEvent();
1463
1464                strcpy(destPath, appData);
1465                PathCat(destPath, "Microsoft\\Internet Explorer\\Quick Launch\\ECERE IDE.lnk");
1466
1467                CreateLink(idePath, destPath, null);//"ECERE IDE");
1468             }
1469          }
1470
1471          // Register File Types
1472          if(associateOptions[AssociateOptions::AssociateEPJ].selected ||
1473             associateOptions[AssociateOptions::AssociateEC].selected ||
1474             associateOptions[AssociateOptions::AssociateC].selected ||
1475             associateOptions[AssociateOptions::AssociateCPP].selected ||
1476             associateOptions[AssociateOptions::AssociateTXT].selected ||
1477             associateOptions[AssociateOptions::Associate3DS].selected ||
1478             associateOptions[AssociateOptions::AssociateIMG].selected)
1479          {
1480             ((GuiApplication)__thisModule).Lock();
1481             installProgress.installing.text = $"Resgistering File Types...";
1482             ((GuiApplication)__thisModule).Unlock();
1483             ((GuiApplication)__thisModule).SignalEvent();
1484             
1485             if(associateOptions[AssociateOptions::AssociateEPJ].selected)
1486             {
1487                AssociateExtension(".epj", "ECERE IDE Project", "epj_file", "Open", idePath);
1488             }
1489             if(associateOptions[AssociateOptions::AssociateEC].selected)
1490             {
1491                AssociateExtension(".ec", "eC Source File", "ec_file", "Open", idePath);
1492                AssociateExtension(".eh", "eC Header File", "eh_file", "Open", idePath);
1493             }
1494             if(associateOptions[AssociateOptions::AssociateC].selected)
1495             {
1496                AssociateExtension(".c", "C Source File", "c_file", "Open", idePath);
1497                AssociateExtension(".h", "C Header File", "h_file", "Open", idePath);
1498             }
1499             if(associateOptions[AssociateOptions::AssociateCPP].selected)
1500             {
1501                AssociateExtension(".cpp", "C++ Source File", "cpp_file", "Open", idePath);
1502                AssociateExtension(".cc", "C++ Source File", "cpp_file", "Open", idePath);
1503                AssociateExtension(".cxx", "C++ Source File", "cpp_file", "Open", idePath);
1504                AssociateExtension(".chh", "C++ Header File", "chh_file", "Open", idePath);
1505                AssociateExtension(".hh", "C++ Header File", "chh_file", "Open", idePath);
1506                AssociateExtension(".hxx", "C++ Header File", "chh_file", "Open", idePath);
1507             }
1508             if(associateOptions[AssociateOptions::AssociateTXT].selected)
1509             {
1510                AssociateExtension(".txt", "Text File", "txt_file", "Open", idePath);
1511             }
1512             if(associateOptions[AssociateOptions::Associate3DS].selected)
1513             {
1514                AssociateExtension(".3ds", "3D Studio Model", "3ds_file", "View", idePath);
1515             }
1516             if(associateOptions[AssociateOptions::AssociateIMG].selected)
1517             {
1518                AssociateExtension(".bmp", "BMP Image", "bmp_file", "View", idePath);
1519                AssociateExtension(".pcx", "PCX Image", "pcx_file", "View", idePath);
1520                AssociateExtension(".png", "PNG Image", "png_file", "View", idePath);
1521                AssociateExtension(".jpg", "JPEG Image", "jpg_file", "View", idePath);
1522                AssociateExtension(".jpeg", "JPEG Image", "jpg_file", "View", idePath);
1523                AssociateExtension(".gif", "GIF Image", "gif_file", "View", idePath);
1524             }
1525          }
1526          ((GuiApplication)__thisModule).Lock();
1527
1528          installProgress.cancel.Destroy(0);
1529          installProgress.finish.text = $"Finish";
1530          installProgress.finish.disabled = false;
1531          installProgress.finish.Activate();
1532          installProgress.installing.text = $"Installation Complete.";
1533          installProgress.title.text = $"Installation Complete";
1534          installProgress.titleInfo.contents = $"Thank you for using the Ecere SDK.";
1535       }
1536       ((GuiApplication)__thisModule).Unlock();
1537       return 0;
1538    }
1539 }