2 static define buildString = $"Ecere SDK v0.44.08 (Without MinGW) -- built on August 9, 2013 ";
4 static define buildString = $"Ecere SDK v0.44.08 -- built on August 9, 2013 ";
7 #define WIN32_LEAN_AND_MEAN
8 #define GetFreeSpace _GetFreeSpace
27 char * OnGetString(char * tempString, void * fieldData, bool * needClass)
31 strcpy(tempString, name);
32 strcat(tempString, " (32)");
38 void OnDisplay(Surface surface, int x, int y, int width, void * fieldData, Alignment alignment, DataDisplayFlags displayFlags)
40 if(!displayFlags.active) { displayFlags.current = false; displayFlags.selected = false; }
41 class::OnDisplay(surface, x + 22, y, width - 22, fieldData, alignment, displayFlags);
45 //#define BUFFERSIZE 0x10000
46 #define BUFFERSIZE 0x1000
47 bool abortInstall = false;
49 void ExtractFileFromArchive(ProgressBar progressBar, char * path, char * outputFile)
51 char fileName[MAX_LOCATION];
52 FileAttribs exists = FileExists(path);
56 if(exists.isDirectory)
58 FileListing listing { path };
62 if(MakeDir(outputFile))
65 FileGetStats(path, &stats);
69 while(listing.Find() && !abortInstall)
71 strcpy(fileName, outputFile);
73 // Tweak file name if out
76 if(!strcmp(fileName, ".")) fileName[0] = '\0';
77 if(listing.name[0] == '/' || listing.name[0] == '\\')
79 char * afterSlash, rest[MAX_LOCATION];
80 for(afterSlash = fileName; *afterSlash == '/' || *afterSlash == '\\'; afterSlash++);
81 strcpy(rest, afterSlash);
82 PathCat(fileName, "_root");
83 PathCat(fileName, rest);
85 else if(listing.name[1] == ':')
88 sprintf(letter, "_%cdrive", toupper(listing.name[0]));
89 PathCat(fileName, letter);
90 PathCat(fileName, listing.name[2] ? (listing.name + 3) : (listing.name + 2));
93 PathCat(fileName, listing.name);
96 PathCat(fileName, listing.name);
97 if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
98 strcpy(fileName, DIR_SEPS);
99 ExtractFileFromArchive(progressBar, listing.path, fileName);
105 File input = FileOpen(path, read);
108 File output = FileOpen(outputFile, write);
111 char fileName[MAX_FILENAME];
112 uint position = progressBar.progress;
115 byte buffer[BUFFERSIZE];
117 FileGetSize(path, &dataSize);
118 GetLastDirectory(outputFile, fileName);
120 ((GuiApplication)__thisModule).SignalEvent();
121 //((GuiApplication)__thisModule).ProcessInput();
122 //((GuiApplication)__thisModule).UpdateDisplay();
124 for(c = 0; c<dataSize && !abortInstall; c += BUFFERSIZE)
126 uint size = (dataSize > c + BUFFERSIZE) ? BUFFERSIZE : (dataSize - c);
128 ((GuiApplication)__thisModule).Unlock();
130 input.Read(buffer, 1, size);
131 output.Write(buffer, 1, size);
133 ((GuiApplication)__thisModule).Lock();
135 progressBar.progress = position + c + size;
136 ((GuiApplication)__thisModule).SignalEvent();
138 //((GuiApplication)__thisModule).ProcessInput();
139 //((GuiApplication)__thisModule).UpdateDisplay();
144 FileGetStats(path, &stats);
150 FileSetTime(outputFile, stats.created, 0, stats.modified);
153 public enum BitArch { none, bits32, bits64 };
159 char * defInstallPath;
160 Component * subComponents;
170 char installPath[MAX_LOCATION];
173 void GetFullPath(char * path)
175 if(this != null && parent)
176 parent->GetFullPath(path);
178 strcpy(path, installDir);
181 PathCat(path, installPath);
184 void Install(char * parentPath, char * parentPath32)
187 if(selected && (arch == none || arch == bits32 || osIS64bit))
189 char path64[MAX_LOCATION];
190 char path32[MAX_LOCATION];
191 strcpy(path64, parentPath);
192 PathCat(path64, installPath);
194 strcpy(path32, parentPath32);
195 PathCat(path32, installPath);
197 installProgress.installing.SetText($"Installing %s...", name);
198 ((GuiApplication)__thisModule).UpdateDisplay();
202 char * path = path64;
203 char source[MAX_LOCATION];
205 strcat(source, dataPath);
212 if(FileExists(source).isFile)
214 char name[MAX_FILENAME];
215 GetLastDirectory(source, name);
220 uint p = installProgress.progressBar.progress;
221 ExtractFileFromArchive(installProgress.progressBar, source, path);
226 for(c = 0; subComponents[c].name; c++)
227 subComponents[c].Install(path64, path32);
234 define minGWIncluded = true;
236 define minGWIncluded = false;
239 Component samples[] =
241 { "Ecere Chess", "samples/chess", "chess", null, false, true, true, none },
242 { "Ecere Fractals", "samples/fractals", "fractals", null, false, true, true, none },
243 { "3D", "samples/3D", "3D", null, false, true, true, none },
244 { "Android", "samples/android", "android", null, false, true, true, none },
245 { "Audio", "samples/audio", "audio", null, false, true, true, none },
246 { "Database", "samples/db", "db", null, false, true, true, none },
247 { "eC", "samples/eC", "eC", null, false, true, true, none },
248 { "Games", "samples/games", "games", null, false, true, true, none },
249 { "GUI & Graphics", "samples/guiAndGfx", "guiAndGfx", null, false, true, true, none },
250 { "Miscellaneous", "samples/misc", "misc", null, false, true, true, none },
251 { "Networking", "samples/net", "net", null, false, true, true, none },
252 { "WIA Scanning", "samples/scanning", "scanning", null, false, true, true, none },
253 { "Threading", "samples/threads", "threads", null, false, true, true, none }
257 public enum CoreSDKID
259 ide, ide32, runtime, runtime32, ec, ec32,
260 gcc, gdb, gdb32, mingw, binutils, make,
264 Component coreSDK[CoreSDKID] =
266 { "Ecere IDE", "ecere-sdk/ide", "bin", null, true, true, true, bits64 },
267 { "Ecere IDE", "ecere-sdk32/ide", "bin", null, true, true, true, bits32 },
268 { "Runtime Library", "ecere-sdk/ecere.dll", "bin", null, true, true, true, bits64 },
269 { "Runtime Library", "ecere-sdk32/ecere.dll", "bin", null, true, true, true, bits32 },
270 { "eC Compiler", "ecere-sdk/compiler", "bin", null, true, true, true, bits64 },
271 { "eC Compiler", "ecere-sdk32/compiler", "bin", null, true, true, true, bits32 },
272 { "GNU C Compiler", "tdm/gcc/core", "tdm", null, true, true, minGWIncluded, none },
273 { "GNU Debugger", "tdm/gdb", "tdm", null, true, true, minGWIncluded, bits64 },
274 { "GNU Debugger", "tdm/gdb32", "tdm", null, true, true, minGWIncluded, bits32 },
275 { "MinGW Runtime", "tdm/mingwrt", "tdm", null, true, true, minGWIncluded, none },
276 { "Binary Utils", "tdm/binutils", "tdm", null, true, true, minGWIncluded, none },
277 { "GNU Make", "tdm/make", "tdm", null, true, true, minGWIncluded, none },
281 public enum AdditionalID
283 eda, eda32, vanilla, vanilla32, extras, upx, gnurx, gnurx32, /*pthreads, */cpp, /*w32api, */gcci18n, gdbi18n, makei18n, binutilsi18n, none
286 Component additional[AdditionalID] =
288 { "Data Access", "ecere-sdk/eda", "bin", null, false, true, true, bits64 },
289 { "Data Access", "ecere-sdk32/eda", "bin", null, false, true, true, bits32 },
290 { "Ecere Vanilla", "ecere-sdk/libecereVanilla.a", "lib", null, false, true, true, bits64 },
291 { "Ecere Vanilla", "ecere-sdk32/libecereVanilla.a", "lib", null, false, true, true, bits32 },
292 { "Ecere Extras", "extras", "extras", null, false, true, true, none },
293 { "UPX", "upx/bin", "upx/bin", null, false, true, true, none },
294 { "GNU Regexp", "tdm/gnurx", "tdm", null, false, true, true, bits64 },
295 { "GNU Regexp", "tdm/gnurx32", "tdm", null, false, true, true, bits32 },
296 // { "pthreads", "tdm/pthreads", "mingw", null, false, true, minGWIncluded, none },
297 { "C++ Compiler", "tdm/gcc/c++", "tdm", null, false, true, minGWIncluded, none },
298 // { "Win32 APIs", "mingw/w32api", "mingw", null, false, true, minGWIncluded, none },
299 { "GCC I18n", "mingw/locale/gcc", "tdm", null, false, false, minGWIncluded, none },
300 { "GDB I18n", "mingw/locale/gdb", "tdm", null, false, false, minGWIncluded, none },
301 { "Make I18n", "mingw/locale/make", "tdm", null, false, false, minGWIncluded, none },
302 { "Binutils I18n", "mingw/locale/binutils", "tdm", null, false, false, minGWIncluded, none },
306 public enum DocumentationID
308 ecereBook, apiRef, tutorials, coursework,
309 gccDoc, gppDocs, gdbDocs, makeDocs, binDocs, mingwDocs, gnurxDocs, upxDocs,
313 Component documentation[DocumentationID] =
315 { "Ecere Book", "ecere-sdk/book", "doc", null, false, true, true, none },
316 { "API Reference", "ecere-sdk/doc", "doc", null, false, true, true, none },
317 { "Ecere Tutorials", "ecere-sdk/tutorials", "doc", null, false, true, true, none },
318 { "Ecere Coursework", "ecere-sdk/coursework", "doc", null, false, true, true, none },
319 { "GCC Docs", "tdm/doc/gcc", "tdm", null, false, false, minGWIncluded, none },
320 { "G++ Docs", "tdm/doc/g++", "tdm", null, false, false, minGWIncluded, none },
321 { "GDB Docs", "tdm/doc/gdb", "tdm", null, false, false, minGWIncluded, none },
322 { "Make Docs", "tdm/doc/make", "tdm", null, false, false, minGWIncluded, none },
323 { "Binutils Docs", "tdm/doc/binutils", "tdm", null, false, false, minGWIncluded, none },
324 { "MinGW Docs", "tdm/doc/mingwrt", "tdm", null, false, false, minGWIncluded, none },
325 { "gnurx Docs", "tdm/doc/gnurx", "tdm", null, false, false, minGWIncluded, none },
326 { "UPX Docs", "upx/doc", "upx/doc", null, false, false, minGWIncluded, none },
330 public enum ComponentID
339 Component components[ComponentID] =
341 { "Core SDK Files", null, null, coreSDK, true, true, true, none },
342 { "Additional Support", null, null, additional, false, true, true, none },
343 { "Documentation", null /*"doc"*/, null /*"doc"*/, documentation, false, true, true, none },
344 { "Samples", null, "samples", samples, false, true, true, none },
348 FileSize totalInstalled;
353 InstallOption * subOptions;
359 enum AssociateOptions
370 InstallOption associateOptions[] =
372 { "Associate with Ecere Project Files (*.epj)", null, true },
373 { "Associate with eC Files (*.ec, *.eh)", null, true },
374 { "Associate with C files (*.c, *.h)", null, false },
375 { "Associate with C++ Files (*.cpp, *.hpp, *.cc, *.hh, *.cxx, *.hxx)", null, false },
376 { "Associate with text files (*.txt)", null, false },
377 { "Associate with 3D Studio Model Files (*.3ds)", null, true },
378 { "Associate with Image Files (*.png, *.jpg, *.pcx, *.bmp, *.gif)", null, false },
384 AddECEREPaths, AddMinGWPaths
387 InstallOption pathOptions[] =
389 { "Add Ecere binaries location to the user environment path", null, true, true },
390 { "Add MinGW to the user environment path", null, true, minGWIncluded }
401 InstallOption options[] =
403 { "Start Menu Group", null, true, true },
404 { "Desktop Icon", null, true, true },
405 { "Quicklaunch Icon", null, true, true },
406 { "Associate the Ecere IDE with Supported File Types", associateOptions, true, true },
407 { "Add binaries location to the user environment paths", pathOptions, true, minGWIncluded },
411 char sourceDir[MAX_LOCATION] = ":";
412 char installDir[MAX_LOCATION];
413 char installDir32[MAX_LOCATION];
416 class Installer : Window
418 text = $"Ecere Software Development Kit Setup - v0.44.08 \"Ryōan-ji\" 64 Bit Edition";
419 background = activeBorder;
424 clientSize = { 636, 456 };
425 // clientSize = { 796, 576 };
426 icon = { ":icon.png" };
428 Picture back { image = BitmapResource { ":ryoanji.png" }, parent = this, position = { 0, 0 } };
429 FileDialog fileDialog
431 master = this, type = selectDir,
432 text = $"Select a new location"
436 master = this, autoCreate = false, inactive = true, text = "...";
438 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
440 DataRow row = componentsBox.currentRow;
441 Component * component = ((CheckItem *)row.GetData(componentField))->data;
442 component->GetFullPath(fileDialog.filePath);
443 StripLastDirectory(fileDialog.filePath, fileDialog.currentDirectory);
445 if(fileDialog.Modal() == ok)
447 componentsBox.StopEditing(false);
448 row.SetData(locationField, fileDialog.filePath);
449 componentsBox.NotifyChanged(this, componentsBox, row);
450 componentsBox.currentField = componentsBox.currentField;
455 CheckListBox componentsBox
457 this, size = { 460, 112 }, position = { 160, 160 }, hasHeader = true;
458 fullRowSelect = false, collapseControl = true, treeBranches = true, rootCollapseButton = true,
461 selectionColor = { 145, 150, 140 };
466 bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
468 if(key == f2 && browse.visible)
469 browse.NotifyClicked(this, browse, 0, 0, 0);
473 bool NotifyChanged(ListBox listBox, DataRow row)
475 Component * component = ((CheckItem *)listBox.GetData(componentField))->data;
476 char path[MAX_LOCATION], relative[MAX_LOCATION] = "", * newPath;
477 char fullPath[MAX_LOCATION];
479 component->parent->GetFullPath(path);
480 strcpy(fullPath, path);
482 newPath = row.GetData(locationField);
485 PathCat(fullPath, newPath);
486 MakePathRelative(fullPath, path, relative);
488 listBox.SetData(locationField, relative);
489 strcpy(component->installPath, relative);
491 //SetAvailableSpace(component, path);
494 install.disabled = false;
495 for(c = 0; components[c].name; c++)
497 SetAvailableSpace(components[c], installDir);
503 bool NotifyEditDone(ListBox listBox, DataRow row)
509 bool NotifyEdited(ListBox listBox, DataRow row)
512 browse.parent = listBox;
513 browse.position = { componentField.width + locationField.width + 18, (listBox.currentIndex+1) * listBox.rowHeight - 2 };
514 browse.size = { 30, listBox.rowHeight + 3 };
515 for(e = listBox.firstChild; e; e = e.next)
517 if(e._class == class(DataBox))
526 void NotifyChecked(CheckListBox listBox, DataRow row)
528 Component * component = ((CheckItem *)row.GetData(componentField))->data;
530 component->selected = listBox.IsChecked(row);
532 if(!component->parent) totalSize -= component->requiredSize;
533 component->requiredSize = 0;
534 if(component->selected)
536 component->requiredSize += component->size;
537 if(component->subComponents)
538 for(c = 0; component->subComponents[c].name; c++)
539 component->requiredSize += component->subComponents[c].requiredSize;
541 if(component->requiredSize)
543 uint requiredSize = component->requiredSize;
544 row.SetData(reqField, requiredSize);
547 row.UnsetData(reqField);
548 if(!component->parent)
550 totalSize += component->requiredSize;
552 char sizeString[100];
553 PrintSize(sizeString, totalSize, 2);
554 totalSpaceValue.text = sizeString;
559 Label agreementLbl { parent = this, text = $"By installing the Ecere SDK, you agree to the .", font = { "Tahoma", 8.25f }, anchor = Anchor { left = 24, top = 424 } };
562 this, inactive = true, offset = false, bevel = false, foreground = blue, font = { "Tahoma", 8.25f, underline = true, bold = true },
563 // text = $"terms and conditions", anchor = Anchor { left = 241, top = 421 };
564 text = $"terms and conditions", anchor = Anchor { left = 237, top = 421 };
565 cursor = ((GuiApplication)__thisModule).GetCursor(hand);
567 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
569 // LicenseBox { master = this, sourceFile = ":ecere-sdk/doc/LICENSE" }.Modal();
570 LicensesForm { master = this }.Modal();
574 CheckListBox optionsBox
576 this, size = { 460, 94 }, position = { 160, 284 };
577 fullRowSelect = false, collapseControl = true, treeBranches = true, rootCollapseButton = true,
582 void NotifyChecked(CheckListBox listBox, DataRow row)
584 CheckItem * item = row.GetData(optionField);
585 InstallOption * option = item->data;
586 option->selected = listBox.IsChecked(row);
591 parent = this, text = $"Install", isDefault = true, size = { 75, 23 }, position = { 432, 416 };
593 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
595 installProgress.Create();
597 // installProgress.thread.Main();
598 installProgress.thread.Create();
602 Button button3 { parent = this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 }, NotifyClicked = ButtonCloseDialog };
603 Label label1 { labeledWindow = destBox, tabCycle = true, isGroupBox = true, parent = this, inactive = false, size = Size { 458, 50 }, anchor = Anchor { left = 160, top = 96 } };
606 parent = label1, master = this, text = $" Destination Folder", size = Size { 336, 22 }, anchor = Anchor { left = 12, top = 20, right = 12 };
607 typeExpected = directory;
608 browseDialog = fileDialog;
611 bool NotifyModified(PathBox pathBox)
614 strcpy(installDir, destBox.path);
615 install.disabled = false;
616 for(c = 0; components[c].name; c++)
618 SetAvailableSpace(components[c], installDir);
625 label1, this, $"Browse", altB, size = { 83, 24 }, position = { 360, 16 };
627 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
629 strcpy(fileDialog.filePath, installDir);
630 StripLastDirectory(installDir, fileDialog.currentDirectory);
631 if(fileDialog.Modal())
633 strcpy(installDir, fileDialog.filePath);
634 destBox.contents = installDir;
635 // TOCHECK: Should setting contents call NotifyModified?
636 destBox.NotifyModified(destBox.master, destBox);
643 this, multiLine = true,
644 opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 96 }, noSelect = true,
645 contents = $"Select the default root\n"
646 "folder where to install\n"
651 this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 132 }, position = { 14, 152 }, noSelect = true,
653 contents = $"Select the optional\n"
654 "components you wish\n"
656 "You may customize the\n"
657 "install location for each\n"
658 "of them, or use the default\n"
659 "based on the main\n"
660 "destination folder."
664 this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 280 }, noSelect = true,
666 contents = $"Select icons to install, file\n"
667 "associations, and system\n"
668 "environment modifications:"
670 Label totalSpaceLabel
672 this, position = { 18, 352 }, text = $"Space Required: "
674 Label totalSpaceValue
676 this, position = { 100, 352 }, text = "0 mb"
680 inactive = true, noSelect = true,
681 multiLine = true, parent = label3, text = "editBox1", opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
682 contents = $"Choose in which folder to install the Ecere SDK, which features\n"
683 "of the SDK to install, as well as where to install program icons."
685 Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
688 image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, text = "picture1", anchor = Anchor { left = 16, top = 4 };
689 cursor = ((GuiApplication)__thisModule).GetCursor(hand);
691 bool OnLeftButtonDown(int x, int y, Modifiers mods)
693 ShellOpen("http://www.ecere.com/");
697 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 } };
698 DataField componentField { "CheckItem", width = 160, header = $"Component" };
699 DataField locationField { "char *", width = 108, header = $"Destination Folder", editable = true };
700 DataField reqField { "FileSize", width = 70, header = $"Req. Space", alignment = right };
701 DataField avField { "FileSize64", width = 70, header = $"Avail. Space", alignment = right };
702 DataField optionField { "CheckItem" };
704 void SetAvailableSpace(Component component, char * parentPath)
706 char path[MAX_LOCATION];
710 strcpy(path, parentPath);
711 PathCat(path, component.installPath);
713 if(component.subComponents)
714 for(c = 0; component.subComponents[c].name; c++)
715 SetAvailableSpace(component.subComponents[c], path);
717 while(!FileExists(path) && path[0])
718 StripLastDirectory(path, path);
721 GetFreeSpace(path, &size);
722 component.row.SetData(avField, size);
723 if(!size) install.disabled = true;
726 FileSize ComputeSize(char * path)
729 FileAttribs attribs = FileExists(path);
730 if(attribs.isDirectory)
732 FileListing listing { path };
733 while(listing.Find())
735 if(listing.stats.attribs.isDirectory)
736 size += ComputeSize(listing.path);
738 size += listing.stats.size;
742 FileGetSize(path, &size);
746 void AddComponent(Component component, Component parent, char * parentPath)
748 DataRow row = (parent != null) ? parent.row.AddRow() : componentsBox.AddRow();
750 FileSize64 avSize = 0;
751 char path[MAX_LOCATION];
754 strcpy(path, parentPath);
755 if(component.defInstallPath)
756 PathCat(path, component.defInstallPath);
757 component.parent = parent;
759 row.SetData(null, CheckItem { component.name, component, (component.arch == bits32 && osIS64bit) } );
761 if(component.defInstallPath)
763 strcpy(component.installPath, component.defInstallPath);
764 ChangeCh(component.installPath, '/', DIR_SEP);
765 row.SetData(locationField, component.installPath);
768 if(component.mandatory)
770 if(component.arch != bits32 || !osIS64bit || component == &coreSDK[gdb32])
771 componentsBox.SetDisabled(row, true);
773 component.selected = false;
775 else if(component.arch == bits32 && osIS64bit)
776 component.selected = false;
778 componentsBox.SetCheck(row, component.selected);
780 if(component.dataPath)
782 char path[MAX_LOCATION];
784 PathCat(path, component.dataPath);
785 component.size = ComputeSize(path);
787 if(component.subComponents)
790 for(c = 0; component.subComponents[c].name; c++)
792 Component * sub = &component.subComponents[c];
793 if(sub->available && (osIS64bit || sub->arch == bits32 || sub->arch == none))
795 AddComponent(sub, component, path);
796 size += sub->requiredSize;
799 sub->selected = false;
803 component.requiredSize = component.selected ? (size + component.size) : 0;
804 if(component.requiredSize)
805 row.SetData(reqField, component.requiredSize);
807 while(!FileExists(path) && path[0])
808 StripLastDirectory(path, path);
811 GetFreeSpace(path, &avSize);
814 row.SetData(avField, avSize);
815 row.collapsed = true;
818 void AddOption(InstallOption option, InstallOption parent)
820 DataRow row = option.row = (parent != null) ? parent.row.AddRow() : optionsBox.AddRow();
821 row.SetData(null, CheckItem { option.name, option } );
822 optionsBox.SetCheck(row, option.selected);
823 if(option.subOptions)
826 for(c = 0; option.subOptions[c].name; c++)
828 AddOption(option.subOptions[c], option);
831 row.collapsed = true;
837 char programFilesDir[MAX_LOCATION];
838 char appData[MAX_LOCATION];
839 char homeDrive[MAX_LOCATION];
840 char winDir[MAX_LOCATION];
842 GetEnvironment("APPDATA", appData, sizeof(appData));
843 GetEnvironment("HOMEDRIVE", homeDrive, sizeof(homeDrive));
844 GetEnvironment("windir", winDir, sizeof(winDir));
846 componentsBox.AddField(componentField);
847 componentsBox.AddField(locationField);
848 componentsBox.AddField(reqField);
849 componentsBox.AddField(avField);
851 optionsBox.AddField(optionField);
853 if(GetEnvironment("ProgramFiles", programFilesDir, MAX_LOCATION))
855 char * x86 = strstr(programFilesDir, " (x86)");
858 strcpy(installDir32, programFilesDir);
859 PathCat(installDir32, "Ecere SDK");
863 strcpy(installDir, programFilesDir);
864 PathCat(installDir, "Ecere SDK");
868 PathCat(installDir, "Ecere SDK");
869 strcpy(installDir, programFilesDir);
872 else if(homeDrive && homeDrive[0])
874 strcpy(installDir, homeDrive);
875 PathCat(installDir, "Ecere SDK");
877 else if(winDir && winDir[0])
879 strcpy(installDir, winDir);
880 PathCat(installDir, "..\\Ecere SDK");
883 strcpy(installDir, "C:\\Ecere SDK");
885 if(appData && appData[0])
887 static char defSamplesPath[MAX_LOCATION];
888 static char defExtrasPath[MAX_LOCATION];
889 strcpy(defSamplesPath, appData);
890 PathCat(defSamplesPath, "Ecere SDK\\Samples");
891 components[samples].defInstallPath = defSamplesPath;
893 strcpy(defExtrasPath, appData);
894 PathCat(defExtrasPath, "Ecere SDK\\extras");
895 additional[extras].defInstallPath = defExtrasPath;
898 destBox.path = installDir;
902 for(c = 0; components[c].name; c++)
903 AddComponent(components[c], null, installDir);
906 // Compute size apart because ToggleCheck will change it
910 for(c = 0; components[c].name; c++)
911 totalSize += components[c].requiredSize;
914 char sizeString[100];
915 PrintSize(sizeString, totalSize, 2);
916 totalSpaceValue.text = sizeString;
918 for(c = 0; options[c].name; c++)
919 AddOption(options[c], null);
928 void OnDrawOverChildren(Surface surface)
930 int tw = label2.size.w;
931 surface.SetForeground(Color { 128, 128, 128 });
932 surface.HLine(label2.position.x + tw + 6, 620, 400);
933 surface.SetForeground(white);
934 surface.HLine(label2.position.x + tw + 6, 621, 401);
935 surface.PutPixel(621, 400);
940 parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
944 class InstallProgress : Window
946 text = $"Ecere Software Development Kit Setup - v0.44.08 \"Ryōan-ji\" 64 Bit Edition";
947 background = activeBorder;
952 // size = Size { 640, 480 };
953 clientSize = { 636, 456 };
954 //clientSize = { 796, 576 };
955 icon = { ":icon.png" };
957 Picture back { image = BitmapResource { ":ryoanji-progress.png" }, parent = this, position = { 0, 0 } };
958 Label installing { this, position = { 32, 160 } };
959 ProgressBar progressBar { parent = this, size = Size { 588, 24 }, anchor = Anchor { left = 24, top = 184 } };
962 parent = this, text = $"Install", disabled = true, isDefault = true, size = Size { 75, 23 }, anchor = Anchor { left = 432, top = 416 };
964 NotifyClicked = ButtonCloseDialog
968 this, text = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 416 };
970 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
978 inactive = true, noSelect = true,
979 multiLine = true, parent = label3, opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
980 contents = $"Please wait while the Ecere Software Development Kit is being installed."
982 Label label2 { parent = this, text = buildString, position = { 16, 392 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
985 image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, anchor = Anchor { left = 16, top = 4 };
986 cursor = ((GuiApplication)__thisModule).GetCursor(hand);
988 bool OnLeftButtonDown(int x, int y, Modifiers mods)
990 ShellOpen("http://www.ecere.com/");
994 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 } };
996 void OnDrawOverChildren(Surface surface)
998 int tw = label2.size.w;
999 surface.SetForeground(Color { 128, 128, 128 });
1000 surface.HLine(label2.position.x + tw + 6, 620, 400);
1001 surface.SetForeground(white);
1002 surface.HLine(label2.position.x + tw + 6, 621, 401);
1003 surface.PutPixel(621, 400);
1008 parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
1010 InstallThread thread
1015 Installer installer {};
1016 InstallProgress installProgress { autoCreate = false };
1018 static void AddPath(char * sysPaths[200], int sysCount, char * paths[200], int * count, char * oldPath, char * userPath, char * path)
1022 for(p = 0; p<sysCount; p++)
1023 if(!fstrcmp(sysPaths[p], path))
1028 for(p = 0; !found && p<*count; p++)
1029 if(!fstrcmp(paths[p], path))
1039 strcat(userPath, ";");
1040 start = paths[(*count)-1] + strlen(paths[(*count)-1])+1;
1045 strcpy(start, path);
1046 *(start + strlen(path)) = '\0';
1047 paths[(*count)++] = start;
1049 strcat(userPath, path);
1054 void ModifyPath(char * systemPath, char * userPath)
1056 char oldPath[8192], * paths[200], * sysPaths[200];
1057 int count, sysCount;
1059 strcpy(oldPath, userPath);
1060 count = TokenizeWith(oldPath, sizeof(paths) / sizeof(char *), paths, ";", false);
1061 sysCount = TokenizeWith(systemPath, sizeof(sysPaths) / sizeof(char *), sysPaths, ";", false);
1065 for(c = 0; coreSDK[c].name; c++)
1068 char path[MAX_LOCATION];
1069 if(!coreSDK[c].selected) continue;
1070 coreSDK[c].GetFullPath(path);
1071 if(c != ide && c != runtime && c != ec &&
1072 c != ide32 && c != runtime32 && c != ec32)
1074 if(!pathOptions[PathOptions::AddMinGWPaths].available || !pathOptions[PathOptions::AddMinGWPaths].selected)
1076 PathCat(path, "bin");
1078 else if(!pathOptions[PathOptions::AddECEREPaths].selected) continue;
1080 AddPath(sysPaths, sysCount, paths, &count, oldPath, userPath, path);
1086 for(c = 0; c <= cpp; c++)
1089 char path[MAX_LOCATION];
1090 if(!additional[c].selected || c == vanilla || c == vanilla32 || c == extras) continue;
1091 if((c != eda && c != eda32 && c != upx) && (!pathOptions[PathOptions::AddMinGWPaths].available || !pathOptions[PathOptions::AddMinGWPaths].selected))
1093 additional[c].GetFullPath(path);
1094 if(c != eda && c != eda32 && c != upx)
1095 PathCat(path, "bin");
1096 AddPath(sysPaths, sysCount, paths, &count, oldPath, userPath, path);
1101 void AssociateExtension(char * extension, char * description, char *name, char * action, char * path)
1107 RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1108 RegSetValueEx(key, null, 0, REG_SZ, name, (uint)strlen(name)+1);
1111 RegCreateKeyEx(HKEY_CLASSES_ROOT, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1112 RegSetValueEx(key, null, 0, REG_SZ, description, (uint)strlen(description)+1);
1115 sprintf(keyName, "%s\\shell", extension);
1116 RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1117 RegSetValueEx(key, null, 0, REG_SZ, action, (uint)strlen(action)+1);
1120 sprintf(keyName, "%s\\shell\\%s", name, action);
1121 RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1124 sprintf(keyName, "%s\\shell\\%s\\command", name, action);
1125 RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1127 sprintf(keyName, path);
1128 strcat(keyName, " \"%L\"");
1130 uint16 wKeyName[2048];
1131 UTF8toUTF16Buffer(keyName, wKeyName, sizeof(wKeyName) / sizeof(uint16));
1132 RegSetValueExW(key, null, 0, REG_SZ, (byte *)wKeyName, (uint)(wcslen(wKeyName) + 1)*sizeof(uint16));
1137 class InstallThread : Thread
1142 ((GuiApplication)__thisModule).Lock();
1143 installProgress.progressBar.range = totalSize;
1144 for(c = 0; components[c].name && !abortInstall; c++)
1145 components[c].Install(installDir, installDir32);
1148 installProgress.progressBar.range = 0;
1149 installProgress.finish.Destroy(0);
1150 installProgress.cancel.text = $"Close";
1151 installProgress.cancel.isDefault = true;
1152 installProgress.cancel.disabled = false;
1153 installProgress.cancel.NotifyClicked = Window::ButtonCloseDialog;
1154 installProgress.installing.text = $"Installation Cancelled.";
1155 installProgress.title.text = $"Installation Cancelled";
1156 installProgress.titleInfo.contents = $"The installation was not completed.";
1160 char idePath[MAX_LOCATION];
1161 char userProfile[MAX_LOCATION];
1164 IDESettings settings = null; // Don't instantiate yet so we can pick up old settings
1166 IDESettingsContainer settingsContainer
1169 dataOwner = &settings;
1170 dataClass = class(IDESettings);
1172 CompilerConfig compiler;
1173 installProgress.installing.text = $"Configuring Ecere IDE...";
1174 ((GuiApplication)__thisModule).Unlock();
1175 ((GuiApplication)__thisModule).SignalEvent();
1177 settingsContainer.Load();
1178 compiler = settings.GetCompilerConfig(defaultCompilerName);
1183 for(c = 0; coreSDK[c].name; c++)
1185 char path[MAX_LOCATION];
1186 if(!coreSDK[c].selected || !coreSDK[c].available) continue;
1188 coreSDK[c].GetFullPath(path);
1189 if(c != ide && c != runtime && c != ec &&
1190 c != ide32 && c != runtime32 && c != ec32)
1191 PathCat(path, "bin");
1192 MakeSlashPath(path);
1193 if((c == ide && osIS64bit) || (c == ide32 && !osIS64bit))
1195 coreSDK[c].GetFullPath(idePath);
1196 PathCat(idePath, "ide.exe");
1199 if(c == runtime || c == runtime32)
1201 if(!compiler.libraryDirs.Find(path))
1202 compiler.libraryDirs.Add(CopyString(path));
1209 for(c = 0; c <= cpp; c++)
1211 char path[MAX_LOCATION];
1212 if(c == extras || !additional[c].selected || !additional[c].available) continue;
1213 additional[c].GetFullPath(path);
1214 if(c != upx && c != eda && c != vanilla && c != eda32 && c != vanilla32)
1215 PathCat(path, "bin");
1216 MakeSlashPath(path);
1217 if(c == vanilla || c == vanilla32)
1219 if(!compiler.libraryDirs.Find(path))
1220 compiler.libraryDirs.Add(CopyString(path));
1224 if(!compiler.executableDirs.Find(path))
1225 compiler.executableDirs.Add(CopyString(path));
1232 char path[MAX_LOCATION] = "";
1234 if(components[samples].selected)
1235 components[samples].GetFullPath(path);
1237 components[coreSDK].GetFullPath(path);
1239 if(!settings.ideProjectFileDialogLocation[0])
1240 settings.ideProjectFileDialogLocation = path;
1241 if(!settings.ideFileDialogLocation[0])
1242 settings.ideFileDialogLocation = path;
1244 if(documentation[apiRef].selected)
1246 documentation[apiRef].GetFullPath(path);
1247 if(!settings.docDir[0])
1248 settings.docDir = path;
1252 settingsContainer.Save();
1253 delete settingsContainer;
1256 // Set up Uninstaller
1257 ((GuiApplication)__thisModule).Lock();
1258 installProgress.installing.text = $"Registering uninstaller...";
1259 ((GuiApplication)__thisModule).Unlock();
1260 ((GuiApplication)__thisModule).SignalEvent();
1265 char * displayName = "Ecere SDK 0.44";
1266 char uninstaller[MAX_LOCATION];
1267 bool nomodify = true;
1269 strcpy(uninstaller, installDir);
1270 PathCat(uninstaller, "uninstall_ecere.exe");
1272 RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Ecere SDK", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1274 RegSetValueEx(key, "DisplayName", 0, REG_SZ, displayName, (uint)strlen(displayName)+1);
1275 RegSetValueEx(key, "UninstallString", 0, REG_SZ, uninstaller, (uint)strlen(uninstaller)+1);
1276 RegSetValueEx(key, "DisplayIcon", 0, REG_SZ, idePath, (uint)strlen(idePath)+1);
1277 //RegSetValueEx(key, "NoModify", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1278 //RegSetValueEx(key, "NoRepair", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1283 if(pathOptions[PathOptions::AddECEREPaths].selected
1285 || pathOptions[PathOptions::AddMinGWPaths].selected
1289 HKEY userKey = null, systemKey = null;
1291 char userPath[8192] = "";
1292 char systemPath[8192] = "";
1293 uint16 wUserPath[8192];
1294 uint16 wSystemPath[8192];
1299 ((GuiApplication)__thisModule).Lock();
1300 installProgress.installing.text = "Registering paths...";
1301 ((GuiApplication)__thisModule).Unlock();
1302 ((GuiApplication)__thisModule).SignalEvent();
1304 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, KEY_QUERY_VALUE, &systemKey) == ERROR_SUCCESS)
1306 size = sizeof(wSystemPath);
1307 RegQueryValueExW(systemKey, L"path", null, null, (byte *)wSystemPath, &size);
1308 UTF16toUTF8Buffer(wSystemPath, systemPath, sizeof(systemPath));
1311 RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &userKey, &status);
1312 if(status == REG_OPENED_EXISTING_KEY)
1314 size = sizeof(wUserPath);
1315 RegQueryValueExW(userKey, L"path", null, null, (byte *)wUserPath, &size);
1316 UTF16toUTF8Buffer(wUserPath, userPath, sizeof(userPath));
1318 ModifyPath(systemPath, userPath);
1319 UTF8toUTF16Buffer(userPath, wUserPath, sizeof(wUserPath) / sizeof(uint16));
1320 RegSetValueExW(userKey, L"path", 0, REG_EXPAND_SZ, (byte *)wUserPath, (uint)(wcslen(wUserPath)+1) * 2);
1321 RegCloseKey(userKey);
1322 RegCloseKey(systemKey);
1324 SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, 0, (int)"Environment", SMTO_NORMAL, 1000, NULL);
1327 // Install Program Group Icons
1328 GetEnvironment("USERPROFILE", userProfile, sizeof(userProfile));
1330 if(options[IconOptions::StartMenuIcon].selected)
1332 char destPath[MAX_LOCATION];
1333 char startMenuPath[MAX_LOCATION] = "";
1336 ((GuiApplication)__thisModule).Lock();
1337 installProgress.installing.text = $"Installing Start Menu Icons...";
1338 ((GuiApplication)__thisModule).Unlock();
1339 ((GuiApplication)__thisModule).SignalEvent();
1341 strcpy(destPath, userProfile);
1343 if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1345 uint16 wStartMenuPath[2048];
1346 uint size = sizeof(wStartMenuPath);
1347 // RegQueryValueEx(key, "Start Menu", null, null, startMenuPath, &size);
1348 RegQueryValueExW(key, L"Programs", null, null, (byte *)wStartMenuPath, &size);
1349 UTF16toUTF8Buffer(wStartMenuPath, startMenuPath, sizeof(startMenuPath));
1352 if(!startMenuPath[0] && userProfile[0])
1354 strcpy(startMenuPath, userProfile);
1355 PathCat(startMenuPath, "Start Menu\\Programs");
1358 if(startMenuPath[0])
1360 strcpy(destPath, startMenuPath);
1361 PathCat(destPath, "Ecere SDK");
1364 strcpy(destPath, startMenuPath);
1365 PathCat(destPath, "Ecere SDK\\Ecere IDE.lnk");
1366 CreateLink(idePath, destPath, null); //"Ecere IDE");
1367 if(components[samples].selected)
1369 char samplesPath[MAX_LOCATION] = "";
1370 components[samples].GetFullPath(samplesPath);
1372 strcpy(destPath, startMenuPath);
1373 PathCat(destPath, "Ecere SDK\\Sample Projects.lnk");
1374 CreateLink(samplesPath, destPath, null);//"Sample Projects");
1376 if(components[documentation].selected && documentation[ecereBook].selected)
1378 char docPath[MAX_LOCATION] = "";
1379 documentation[ecereBook].GetFullPath(docPath);
1380 PathCat(docPath, "Ecere Tao of Programming [work in progress].pdf");
1383 char tao[MAX_LOCATION] ;
1384 documentation[ecereBook].GetFullPath(tao);
1385 PathCat(tao, "tao.pdf");
1386 RenameFile(tao, docPath);
1389 strcpy(destPath, startMenuPath);
1390 PathCat(destPath, "Ecere SDK\\The Ecere Tao of Programming.lnk");
1391 CreateLink(docPath, destPath, null);
1396 // Install Desktop Icon
1397 if(options[IconOptions::DesktopIcon].selected)
1400 char desktopPath[MAX_LOCATION];
1402 if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
1404 uint16 wDesktopPath[MAX_LOCATION];
1405 uint size = sizeof(wDesktopPath);
1406 RegQueryValueExW(key, L"Desktop", null, null, (byte *)wDesktopPath, &size);
1407 UTF16toUTF8Buffer(wDesktopPath, desktopPath, sizeof(desktopPath));
1410 if(!desktopPath[0] && userProfile[0])
1412 strcpy(desktopPath, userProfile);
1413 PathCat(desktopPath, "Desktop");
1418 PathCat(desktopPath, "Ecere IDE.lnk");
1420 ((GuiApplication)__thisModule).Lock();
1421 installProgress.installing.text = $"Installing Desktop Icon...";
1422 ((GuiApplication)__thisModule).Unlock();
1423 ((GuiApplication)__thisModule).SignalEvent();
1425 CreateLink(idePath, desktopPath, null);//"Ecere IDE");
1429 // Install QuickLaunch Icon
1430 if(options[IconOptions::QuickLaunchIcon].selected)
1432 char appData[MAX_LOCATION];
1433 GetEnvironment("APPDATA", appData, sizeof(appData));
1437 char destPath[MAX_LOCATION];
1439 ((GuiApplication)__thisModule).Lock();
1440 installProgress.installing.text = $"Installing Quicklaunch Icon...";
1441 ((GuiApplication)__thisModule).Unlock();
1442 ((GuiApplication)__thisModule).SignalEvent();
1444 strcpy(destPath, appData);
1445 PathCat(destPath, "Microsoft\\Internet Explorer\\Quick Launch\\Ecere IDE.lnk");
1447 CreateLink(idePath, destPath, null);//"Ecere IDE");
1451 // Register File Types
1452 if(associateOptions[AssociateOptions::AssociateEPJ].selected ||
1453 associateOptions[AssociateOptions::AssociateEC].selected ||
1454 associateOptions[AssociateOptions::AssociateC].selected ||
1455 associateOptions[AssociateOptions::AssociateCPP].selected ||
1456 associateOptions[AssociateOptions::AssociateTXT].selected ||
1457 associateOptions[AssociateOptions::Associate3DS].selected ||
1458 associateOptions[AssociateOptions::AssociateIMG].selected)
1460 ((GuiApplication)__thisModule).Lock();
1461 installProgress.installing.text = $"Resgistering File Types...";
1462 ((GuiApplication)__thisModule).Unlock();
1463 ((GuiApplication)__thisModule).SignalEvent();
1465 if(associateOptions[AssociateOptions::AssociateEPJ].selected)
1467 AssociateExtension(".epj", "Ecere IDE Project", "epj_file", "Open", idePath);
1469 if(associateOptions[AssociateOptions::AssociateEC].selected)
1471 AssociateExtension(".ec", "eC Source File", "ec_file", "Open", idePath);
1472 AssociateExtension(".eh", "eC Header File", "eh_file", "Open", idePath);
1474 if(associateOptions[AssociateOptions::AssociateC].selected)
1476 AssociateExtension(".c", "C Source File", "c_file", "Open", idePath);
1477 AssociateExtension(".h", "C Header File", "h_file", "Open", idePath);
1479 if(associateOptions[AssociateOptions::AssociateCPP].selected)
1481 AssociateExtension(".cpp", "C++ Source File", "cpp_file", "Open", idePath);
1482 AssociateExtension(".cc", "C++ Source File", "cpp_file", "Open", idePath);
1483 AssociateExtension(".cxx", "C++ Source File", "cpp_file", "Open", idePath);
1484 AssociateExtension(".chh", "C++ Header File", "chh_file", "Open", idePath);
1485 AssociateExtension(".hh", "C++ Header File", "chh_file", "Open", idePath);
1486 AssociateExtension(".hxx", "C++ Header File", "chh_file", "Open", idePath);
1488 if(associateOptions[AssociateOptions::AssociateTXT].selected)
1490 AssociateExtension(".txt", "Text File", "txt_file", "Open", idePath);
1492 if(associateOptions[AssociateOptions::Associate3DS].selected)
1494 AssociateExtension(".3ds", "3D Studio Model", "3ds_file", "View", idePath);
1496 if(associateOptions[AssociateOptions::AssociateIMG].selected)
1498 AssociateExtension(".bmp", "BMP Image", "bmp_file", "View", idePath);
1499 AssociateExtension(".pcx", "PCX Image", "pcx_file", "View", idePath);
1500 AssociateExtension(".png", "PNG Image", "png_file", "View", idePath);
1501 AssociateExtension(".jpg", "JPEG Image", "jpg_file", "View", idePath);
1502 AssociateExtension(".jpeg", "JPEG Image", "jpg_file", "View", idePath);
1503 AssociateExtension(".gif", "GIF Image", "gif_file", "View", idePath);
1506 ((GuiApplication)__thisModule).Lock();
1508 installProgress.cancel.Destroy(0);
1509 installProgress.finish.text = $"Finish";
1510 installProgress.finish.disabled = false;
1511 installProgress.finish.Activate();
1512 installProgress.installing.text = $"Installation Complete.";
1513 installProgress.title.text = $"Installation Complete";
1514 installProgress.titleInfo.contents = $"Thank you for using the Ecere SDK.";
1516 ((GuiApplication)__thisModule).Unlock();