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