ide; (#1056) fix buttons overlap on list for post-build/install commands in project...
[sdk] / ide / src / ProjectSettings.ec
index e426c21..576fd63 100644 (file)
@@ -2,23 +2,39 @@ import "ide"
 import "WorkspaceSettings"
 import "ProjectTabSettings"
 import "StringsBox"
-import "SelectorBar"
+// import "SelectorBar"
 
 static ProjectConfig config;
 static Platform platform;
 static ProjectNode currentNode;
 static Project project;
 
-static String MakeString(char * s, int len)
+static String MakeString(char * s, int len, char * switchToKeep, int lenSwitchToKeep)
 {
    String string = new char[len+1];
-   memcpy(string, s, len);
-   string[len] = 0;
+   if(s[0] == '-' && switchToKeep && switchToKeep[0])
+   {
+      if(strstr(s+1, switchToKeep) == s+1)
+      {
+         memcpy(string, s+lenSwitchToKeep+1, len-lenSwitchToKeep-1);
+         string[len-lenSwitchToKeep-1] = '\0';
+      }
+      else
+         delete string;
+   }
+   else
+   {
+      memcpy(string, s, len);
+      string[len] = '\0';
+   }
    return string;
 }
 
 class StringListBox : EditBox
 {
+   char * switchToKeep;
+   int lenSwitchToKeep;
+
    textHorzScroll = true;
 
    property Array<String> strings
@@ -31,15 +47,13 @@ class StringListBox : EditBox
             bool first = true;
             for(item : value)
             {
-               bool quoted = strchr(item, ' ') != null;
-               if(!first)
-                  AddS(" ");
-               if(quoted)
-                  AddS("\"");
-               AddS(item);
-               if(quoted)
-                  AddS("\"");
-               first = false;
+               if(item)
+               {
+                  if(!first)
+                     AddS(" ");
+                  AddS(item);
+                  first = false;
+               }
             }
          }
       }
@@ -48,6 +62,7 @@ class StringListBox : EditBox
          Array<String> array { };
          int c, start = 0;
          char * contents = property::contents;
+         char * s;
          char ch;
          bool quoted = false;
 
@@ -56,43 +71,41 @@ class StringListBox : EditBox
             if(ch == ' ' && !quoted)
             {
                if(c - start)
-                  array.Add(MakeString(contents + start, c - start));
+               {
+                  if((s = MakeString(contents + start, c - start, switchToKeep, lenSwitchToKeep)))
+                     array.Add(s);
+               }
                start = c + 1;
             }
             else if(ch == '\"')
             {
                if(quoted)
-               {
-                  if(c - start)
-                     array.Add(MakeString(contents + start, c - start));
                   quoted = false;
-                  start = c + 1;
-               }
                else
-               {
                   quoted = true;
-                  start = c + 1;
-               }
             }
          }
          if(c - start)
-            array.Add(MakeString(contents + start, c - start));
+         {
+            if((s = MakeString(contents + start, c - start, switchToKeep, lenSwitchToKeep)))
+               array.Add(s);
+         }
          return array;
       }
    }
 }
 
-define dialogTitle = "Project Settings";
+define dialogTitle = $"Project Settings";
 static Color unfocusedSelectorColor { 70, 96, 166 };
 class ProjectSettings : Window
 {
    text = dialogTitle;
-   background = activeBorder;
+   background = formColor;
    borderStyle = sizable;
-   minClientSize = { 650, 490 };
+   minClientSize = { 650, 520 };
    hasClose = true;
    tabCycle = true;
-   size = { 650, 490 };
+   size = { 650, 520 };
 
    property Project project
    {
@@ -124,7 +137,7 @@ class ProjectSettings : Window
       char * nodeName = currentNode && currentNode != project.topNode ? currentNode.name : "";
       char * config = buildTab.selectedConfigName;
       char * platform = buildTab.selectedPlatformName;
-      char * label = new char[strlen(dialogTitle) + 3 + strlen(project.topNode.name) + 3 + 
+      char * label = new char[strlen(dialogTitle) + 3 + strlen(project.topNode.name) + 3 +
                               strlen(nodeName) + 2 + strlen(config) + 1 + strlen(platform) + 1 + 1];
       strcpy(label, dialogTitle);
       strcat(label, " - ");
@@ -159,7 +172,7 @@ class ProjectSettings : Window
 
    TabControl prjTabControl
    {
-      this, background = activeBorder, anchor = { left = 8, top = 4, right = 8, bottom = 38 };
+      this, background = formColor, anchor = { left = 8, top = 4, right = 8, bottom = 38 };
    };
    ProjectTab projectTab { this, tabControl = prjTabControl };
    BuildTab buildTab { this, tabControl = prjTabControl };
@@ -169,7 +182,7 @@ class ProjectSettings : Window
    {
       this, size = { 80, 22 };
       anchor = { right = 8, bottom = 8 };
-      text = "Cancel", hotKey = escape, id = DialogResult::cancel;
+      text = $"Cancel", hotKey = escape, id = DialogResult::cancel;
 
       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
       {
@@ -178,8 +191,8 @@ class ProjectSettings : Window
             DialogResult diagRes = MessageBox
             {
                type = okCancel, master = ide,
-               text = "Lose Changes?",
-               contents = "Are you sure you wish to discard changes made to the build options?"
+               text = $"Lose Changes?",
+               contents = $"Are you sure you wish to discard changes made to the build options?"
             }.Modal();
             if(diagRes == ok)
             {
@@ -208,7 +221,7 @@ class ProjectSettings : Window
    {
       this, size = { 80, 22 };
       anchor = { right = 96, bottom = 8 };
-      text = "OK", isDefault = true;
+      text = $"OK", isDefault = true;
 
       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
       {
@@ -240,6 +253,10 @@ class ProjectSettings : Window
    {
       UpdateDialogTitle();
       prjTabControl.curTab = buildTab;
+
+      ((DirectoriesBox)buildTab.compilerTab.includeDirs.editor).baseBrowsePath = project.topNode.path;
+      ((DirectoriesBox)buildTab.linkerTab.libraryDirs.editor).baseBrowsePath = project.topNode.path;
+
       return true;
    }
 }
@@ -248,6 +265,9 @@ class ProjectSettings : Window
 
 // TOFIX: USING T INSTEAD OF Z HERE CAUSED US SOME CONFLICTS WITH T IN Array TEMPLATES
 
+// the BlendFileConfigPlatformProjectOptions function and the GenericOptionTools class
+// contain code that is closely matched to the following code
+// output changing modification should be mirrored in both implementations
 class OptionBox<class Z> : CommonControl
 {
    bool mergeValues, configReplaces;
@@ -272,7 +292,7 @@ class OptionBox<class Z> : CommonControl
    }
 
    property bool visible { set { editor.visible = value; } get { return editor.visible; } }
-   property Window parent { set { editor.parent = value; Window::parent = value; master = value; editor.id = (int)this; } }
+   property Window parent { set { editor.parent = value; Window::parent = value; master = value; editor.id = (int64)this; } }
    property Point position { set { editor.position = value; } }
    property Size size { set { editor.size = value; } }
    property Anchor anchor { set { editor.anchor = value; } }
@@ -285,16 +305,27 @@ class OptionBox<class Z> : CommonControl
    Menu clearMenu { };
    MenuItem clearItem
    {
-      clearMenu, "Clear";
+      clearMenu, $"Clear";
 
       bool NotifySelect(MenuItem selection, Modifiers mods)
       {
          OptionBox ob = (OptionBox)id;
-         ob.Unset();
+         if(eClass_IsDerived(ob._class, class(CheckBoxForEnumOptionBox)))
+         {
+            Window slave;
+            for(slave = ob.master.firstSlave; slave; slave = slave.nextSlave)
+            {
+               if(eClass_IsDerived(slave._class, class(CheckBoxForEnumOptionBox)) &&
+                  ((OptionBox)slave).option == ob.option)
+                     ((OptionBox)slave).Unset();
+            }
+         }
+         else
+            ob.Unset();
          return true;
       }
    };
-   
+
    bool Window::OptionBox_OnRightButtonDown(int x, int y, Modifiers mods)
    {
       OptionBox ob = (OptionBox)id;
@@ -316,6 +347,7 @@ class OptionBox<class Z> : CommonControl
       return (((bool(*)(Window, Key, unichar)) ob.chainKeyDown)(this, key, ch);
    }
 
+   // code: 0 = not set anywhere, 1 = overridden here, 2 = inherited
    void SetAttribs(int code)
    {
       Window c;
@@ -330,7 +362,7 @@ class OptionBox<class Z> : CommonControl
                break;
          }
       }
-      
+
       if(!c)
       {
          label = null;
@@ -380,7 +412,7 @@ class OptionBox<class Z> : CommonControl
    virtual void FinalizeLoading();
 
    virtual void LoadOption(ProjectOptions options);
-   virtual void RetrieveOption(ProjectOptions options);
+   virtual void RetrieveOption(ProjectOptions options, bool isCfgOrPlt);
    virtual void UnsetOption(ProjectOptions options)
    {
       Z value = (Z)0;
@@ -401,7 +433,7 @@ class OptionBox<class Z> : CommonControl
    {
       return OptionSet(options);
    }
-   
+
    void MarkBuildTabModified()
    {
       BuildTab buildTab = (BuildTab)master;
@@ -409,7 +441,7 @@ class OptionBox<class Z> : CommonControl
          buildTab = (BuildTab)buildTab.master;
       if(buildTab) buildTab.modifiedDocument = true;
    }
-   
+
    void Unset()
    {
       char * platformName = platform ? platform.OnGetString(0,0,0) : null;
@@ -440,7 +472,7 @@ class OptionBox<class Z> : CommonControl
                            Iterator<PlatformOptions> it { c.platforms };
                            if(it.Find(p))
                            {
-                              it.Remove(p);
+                              it.Remove();
                               delete p;
                            }
                         }
@@ -505,7 +537,11 @@ class OptionBox<class Z> : CommonControl
       if(currentNode.options && OptionSet(currentNode.options))
          UnsetOption(currentNode.options);
       if(currentNode.options && currentNode.options.isEmpty)
-         delete currentNode.options;
+      {
+         // delete currentNode.options;
+         // Property will free:
+         currentNode.options = null;
+      }
 
       Load();
    }
@@ -517,6 +553,7 @@ class OptionBox<class Z> : CommonControl
       bool skipped = false;
       for(node = currentNode; node; node = node.parent)
       {
+         bool configXplatformSet = false;
          if(config && node.configurations)
          {
             for(c : node.configurations; !strcmpi(c.name, config.name))
@@ -525,12 +562,16 @@ class OptionBox<class Z> : CommonControl
                {
                   for(p : c.platforms; !strcmpi(p.name, platformName))
                   {
-                     if(skipped && p.options && OptionSet(p.options))
-                        LoadOption(p.options);
+                     if(p.options && OptionSet(p.options))
+                     {
+                        if(skipped)
+                           LoadOption(p.options);
+                     }
+                     configXplatformSet = true;
                      skipped = true;
                      break;
                   }
-               }               
+               }
 
                if(skipped && c.options && OptionSet(c.options))
                {
@@ -541,7 +582,7 @@ class OptionBox<class Z> : CommonControl
                break;
             }
          }
-         if(platform && node.platforms)
+         if((!configXplatformSet || !configReplaces) && platform && node.platforms)
          {
             for(p : node.platforms; !strcmpi(p.name, platformName))
             {
@@ -580,12 +621,12 @@ class OptionBox<class Z> : CommonControl
                c.platforms.Add(p = PlatformOptions { CopyString(platformName) });
 
             if(!p.options) p.options = { };
-            RetrieveOption(p.options);
+            RetrieveOption(p.options, true);
             if(!mergeValues) SetAttribs(1);
             return;
          }
          if(!c.options) c.options = { };
-         RetrieveOption(c.options);
+         RetrieveOption(c.options, true);
          if(!mergeValues) SetAttribs(1);
          return;
       }
@@ -598,13 +639,13 @@ class OptionBox<class Z> : CommonControl
             currentNode.platforms.Add(p = PlatformOptions { CopyString(platformName) });
 
          if(!p.options) p.options = { };
-         RetrieveOption(p.options);
+         RetrieveOption(p.options, true);
          if(!mergeValues) SetAttribs(1);
          return;
       }
 
       if(!currentNode.options) currentNode.options = { };
-      RetrieveOption(currentNode.options);
+      RetrieveOption(currentNode.options, false);
       if(!mergeValues) SetAttribs((currentNode.parent || OptionCheck(currentNode.options)) ? 1 : 0);
    }
 
@@ -615,6 +656,7 @@ class OptionBox<class Z> : CommonControl
       bool setAttribs = false;
       for(node = currentNode; node; node = node.parent)
       {
+         bool configXplatformSet = false;
          ProjectConfig nodeConfig = null;
          if(config && node.configurations)
          {
@@ -629,16 +671,17 @@ class OptionBox<class Z> : CommonControl
                         LoadOption(p.options);
                         if(!setAttribs) { setAttribs = true; SetAttribs((node == currentNode) ? 1 : 2); }
                         if(!mergeValues) { FinalizeLoading(); return; }
+                        configXplatformSet = true;
                      }
                      break;
                   }
-               }               
+               }
 
                nodeConfig = c;
                break;
             }
          }
-         if(platform && node.platforms)
+         if(platform && node.platforms && (!configXplatformSet || !configReplaces))
          {
             for(p : node.platforms; !strcmpi(p.name, platformName))
             {
@@ -696,7 +739,7 @@ class StringOptionBox : OptionBox<String>
       textHorzScroll = true;
    };
 
-   void RetrieveOption(ProjectOptions options)
+   void RetrieveOption(ProjectOptions options, bool isCfgOrPlt)
    {
       String * string = (String*)((byte *)options + option);
       if(*string) delete *string;
@@ -738,12 +781,13 @@ class PathOptionBox : OptionBox<String>
 
       bool NotifyModified(PathBox pathBox)
       {
+         FixPathOnPathBoxNotifyModified(pathBox);
          ((OptionBox)pathBox.id).Retrieve();
          return true;
       }
    };
 
-   void RetrieveOption(ProjectOptions options)
+   void RetrieveOption(ProjectOptions options, bool isCfgOrPlt)
    {
       String * string = (String*)((byte *)options + option);
       String slashPath = ((PathBox)editor).slashPath;
@@ -781,7 +825,7 @@ class MultiStringOptionBox : OptionBox<Array<String>>
 
    Array<String> tempStrings;
 
-   void RetrieveOption(ProjectOptions options)
+   void RetrieveOption(ProjectOptions options, bool isCfgOrPlt)
    {
       Array<String> newStrings = GetStrings();
       Array<String> * strings = (Array<String>*)((byte *)options + option);
@@ -800,7 +844,7 @@ class MultiStringOptionBox : OptionBox<Array<String>>
             {
                String s = it.data;
                bool found = false;
-               for(i : tempStrings; !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
+               for(i : tempStrings; i && s && !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
                if(found && (!configReplaces || platform))   // ADDED || platform here...
                {
                   delete s;
@@ -811,7 +855,13 @@ class MultiStringOptionBox : OptionBox<Array<String>>
          delete tempStrings;
       }
 
-      *strings = newStrings;
+      if(!mergeValues || (configReplaces && isCfgOrPlt && !platform))
+         *strings = newStrings;
+      else
+      {
+         *strings = (newStrings && newStrings.count) ? newStrings : null;
+         if(newStrings && !newStrings.count) delete newStrings;
+      }
 
       Load();
    }
@@ -828,11 +878,11 @@ class MultiStringOptionBox : OptionBox<Array<String>>
             for(s : strings)
             {
                bool found = false;
-               for(i : tempStrings; !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
+               for(i : tempStrings; i && s && !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
                if(!found) tempStrings.Add(s);
             }
          }
-      }         
+      }
       else
       {
          SetStrings(options ? *(Array<String>*)((byte *)options + option) : null);
@@ -886,6 +936,8 @@ class StringArrayOptionBox : MultiStringOptionBox
    // NO VIRTUAL PROPERTIES YET...
    Array<String> GetStrings() { return ((StringListBox)editor).strings; }
    void SetStrings(Array<String> value) { ((StringListBox)editor).strings = value; }
+
+   property char * switchToKeep { set { ((StringListBox)editor).switchToKeep = value; ((StringListBox)editor).lenSwitchToKeep = strlen(value); } };
 }
 
 class StringsArrayOptionBox : MultiStringOptionBox
@@ -909,68 +961,113 @@ class StringsArrayOptionBox : MultiStringOptionBox
    void SetStrings(Array<String> value) { ((StringsBox)editor).strings = value; }
 }
 
-class DirsArrayOptionBox : MultiStringOptionBox
+bool eString_IsPathRelatedTo(char * path, char * to)
 {
-   editor = DirectoriesBox
+   if(path[0] && to[0])
    {
-      bool NotifyModified(DirectoriesBox dirsBox)
+      char rest[MAX_FILENAME];
+      char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION] = "";
+      char toPart[MAX_FILENAME], toRest[MAX_LOCATION] = "";
+      SplitDirectory(path, pathPart, pathRest);
+      SplitDirectory(to, toPart, toRest);
+      if(!fstrcmp(pathPart, toPart))
       {
-         ((OptionBox)dirsBox.id).Retrieve();
-         return true;
-      }
-
-      bool OnChangedDir(char * * directory)
-      {
-         char fixedDirectory[MAX_LOCATION] = "";
-         if(PathCat(fixedDirectory, *directory))
+         if(pathRest[0] && toRest[0])
          {
-            char cwdBackup[MAX_LOCATION];
-            if(project)
-            {
-               GetWorkingDir(cwdBackup, sizeof(cwdBackup));
-               ChangeWorkingDir(project.topNode.path);
-            }
-            FileFixCase(fixedDirectory);
-            if(project)
-               ChangeWorkingDir(cwdBackup);
-            delete *directory;
-            *directory = CopyString(fixedDirectory);
-            return true;
+            SplitDirectory(pathRest, pathPart, pathRest);
+            SplitDirectory(toRest, toPart, toRest);
+            if(!fstrcmp(pathPart, toPart))
+               return true;
          }
-         return false;
       }
+   }
+   return false;
+}
+
+static void FixPathOnPathBoxNotifyModified(PathBox pathBox)
+{
+   int len;
+   char path[MAX_LOCATION];
+   ValidPathBufCopy(path, pathBox.path);
+   len = strlen(path);
+   if(len && !(path[0] == '.' && (len == 1 || (len == 2 && path[1] == DIR_SEP) || (len > 1 && path[1] == '.'))))
+   {
+      char cwdBackup[MAX_LOCATION];
+      if(project)
+      {
+         GetWorkingDir(cwdBackup, sizeof(cwdBackup));
+         ChangeWorkingDir(project.topNode.path);
+      }
+      FileFixCase(path);
+      if(project)
+         ChangeWorkingDir(cwdBackup);
+      if(eString_IsPathRelatedTo(path, project.topNode.path))
+         MakePathRelative(path, project.topNode.path, path);
+      if(!path[0])
+         strcpy(path, ".");
+      len = strlen(path);
+   }
+   if(len>1 && path[len-1] == DIR_SEP)
+      path[--len] = '\0';
+   pathBox.path = path;
+}
+
+class DirsArrayOptionBox : MultiStringOptionBox
+{
+public:
+   property char * switchToKeep { set { switchToKeep = value; lenSwitchToKeep = strlen(value); } };
+private:
+   char * switchToKeep;
+   int lenSwitchToKeep;
 
-      bool OnPrepareBrowseDir(char * * directory)
+   editor = DirectoriesBox
+   {
+      browseDialog = { };
+      bool NotifyModified(DirectoriesBox dirsBox)
       {
-         char dir[MAX_LOCATION];
-         if(project)
+         char * switchToKeep = ((DirsArrayOptionBox)dirsBox.id).switchToKeep;
+         if(switchToKeep && switchToKeep[0])
          {
-            GetSystemPathBuffer(dir, project.topNode.path);
-            if(*directory)
-               PathCat(dir, *directory);
+            bool change = false;
+            int lenSwitchToKeep = ((DirsArrayOptionBox)dirsBox.id).lenSwitchToKeep;
+            Array<String> dirs { };
+            Array<String> previousDirs = dirsBox.strings;
+            for(d : previousDirs)
+            {
+               int c;
+               char * buffer = new char[strlen(d)+64];
+               char * tokens[1024];
+               uint count;
+               strcpy(buffer, d);
+               count = Tokenize(buffer, sizeof(tokens)/sizeof(tokens[0]), tokens, (BackSlashEscaping)false);
+               for(c=0; c<count; c++)
+               {
+                  if(tokens[c][0] == '-')
+                  {
+                     if(strstr(tokens[c]+1, switchToKeep) == tokens[c]+1)
+                        tokens[c] += lenSwitchToKeep+1;
+                     else
+                        tokens[c][0] = '\0';
+                     change = true;
+                  }
+                  dirs.Add(CopyString(tokens[c]));
+               }
+               delete buffer;
+            }
+            if(change)
+               dirsBox.strings = dirs;
+            dirs.Free();
+            delete dirs;
+            previousDirs.Free();
+            delete previousDirs;
          }
-         else if(*directory)
-            strcpy(dir, *directory);
-         else
-            dir[0] = '\0';
-         
-         delete *directory;
-         *directory = CopyString(dir);
-
-            // GCC 4.4 bug:  -----  path becomes *directory
-            //strcpy(dir, path ? path : "");
+         ((OptionBox)dirsBox.id).Retrieve();
          return true;
       }
 
-      bool OnBrowsedDir(char * * directory)
+      bool NotifyPathBoxModified(DirectoriesBox dirsBox, PathBox pathBox)
       {
-         if(project)
-         {
-            char path[MAX_LOCATION];
-            MakePathRelative(*directory, project.topNode.path, path);
-            delete *directory;
-            *directory = CopyString(path);
-         }
+         FixPathOnPathBoxNotifyModified(pathBox);
          return true;
       }
    };
@@ -997,10 +1094,11 @@ class BoolOptionBox : OptionBox<SetBool>
       return *(SetBool*)((byte *)options + option) == true;
    }
 
-   void RetrieveOption(ProjectOptions options)
+   void RetrieveOption(ProjectOptions options, bool isCfgOrPlt)
    {
       bool checked = ((Button)editor).checked;
-      *(SetBool*)((byte *)options + option) = checked ? true : (currentNode.parent ? false : unset);
+      *(SetBool*)((byte *)options + option) = checked ? true :
+         ((currentNode.parent || isCfgOrPlt) ? false : unset);
    }
 
    void LoadOption(ProjectOptions options)
@@ -1009,6 +1107,46 @@ class BoolOptionBox : OptionBox<SetBool>
    }
 }
 
+class CheckBoxForEnumOptionBox : OptionBox
+{
+   editor = Button
+   {
+      isCheckbox = true;
+
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         ((OptionBox)button.id).Retrieve();
+         {
+            Window slave;
+            for(slave = master.firstSlave; slave; slave = slave.nextSlave)
+            {
+               if(eClass_IsDerived(slave._class, class(CheckBoxForEnumOptionBox)) &&
+                     slave != (Window)button.id &&
+                     ((OptionBox)slave).option == ((OptionBox)button.id).option)
+                  ((OptionBox)slave).Load();
+            }
+         }
+         return true;
+      }
+   };
+
+   Z enumValue;
+   void LoadOption(ProjectOptions options)
+   {
+      Z value = options ? *(Z*)((byte *)options + option) : (Z)0;
+      ((Button)editor).checked = value == enumValue;
+   }
+
+   void RetrieveOption(ProjectOptions options, bool isCfgOrPlt)
+   {
+      Button checkBox = (Button)editor;
+      if(checkBox.checked)
+         *(Z*)((byte *)options + option) = enumValue;
+   }
+}
+
+class BuildBitDepthOptionBox : CheckBoxForEnumOptionBox<BuildBitDepth> { }
+
 class DropOptionBox : OptionBox
 {
    editor = DropBox
@@ -1018,16 +1156,16 @@ class DropOptionBox : OptionBox
          ((OptionBox)dropBox.id).Retrieve();
          return true;
       }
-   };   
+   };
 
    void LoadOption(ProjectOptions options)
    {
       DropBox dropBox = (DropBox)editor;
       Z value = options ? *(Z*)((byte *)options + option) : (Z)0;
-      dropBox.currentRow = value ? dropBox.FindRow((int)value) : dropBox.firstRow;
+      dropBox.currentRow = value ? dropBox.FindRow((int64)value) : dropBox.firstRow;
    }
 
-   void RetrieveOption(ProjectOptions options)
+   void RetrieveOption(ProjectOptions options, bool isCfgOrPlt)
    {
       DropBox dropBox = (DropBox)editor;
       DataRow row = dropBox.currentRow;
@@ -1044,15 +1182,15 @@ class TargetTypeDB : DropOptionBox<TargetTypes>
 
       row = ((DropBox)editor).AddRow();
       row.tag = TargetTypes::executable;
-      row.SetData(null, "Executable");
+      row.SetData(null, $"Executable");
 
       row = ((DropBox)editor).AddRow();
       row.tag = TargetTypes::sharedLibrary;
-      row.SetData(null, "Shared Library");
+      row.SetData(null, $"Shared Library");
 
       row = ((DropBox)editor).AddRow();
       row.tag = TargetTypes::staticLibrary;
-      row.SetData(null, "Static Library");
+      row.SetData(null, $"Static Library");
    }
 
    bool OptionCheck(ProjectOptions options)
@@ -1069,15 +1207,15 @@ class OptimizationDB : DropOptionBox<OptimizationStrategy>
       DataRow row;
       row = ((DropBox)editor).AddRow();
       row.tag = OptimizationStrategy::none;
-      row.SetData(null, "None");
+      row.SetData(null, $"None");
 
       row = ((DropBox)editor).AddRow();
       row.tag = OptimizationStrategy::speed;
-      row.SetData(null, "For Speed (-O2)");
+      row.SetData(null, $"For Speed (-O2)");
 
       row = ((DropBox)editor).AddRow();
       row.tag = OptimizationStrategy::size;
-      row.SetData(null, "For Size (-Os)");
+      row.SetData(null, $"For Size (-Os)");
    }
 
    bool OptionCheck(ProjectOptions options)
@@ -1094,15 +1232,15 @@ class WarningsDB : DropOptionBox<WarningsOption>
       DataRow row;
       row = ((DropBox)editor).AddRow();
       row.tag = WarningsOption::normal;
-      row.SetData(null, "Normal");
+      row.SetData(null, $"Normal");
 
       row = ((DropBox)editor).AddRow();
       row.tag = WarningsOption::none;
-      row.SetData(null, "None");
+      row.SetData(null, $"None");
 
       row = ((DropBox)editor).AddRow();
       row.tag = WarningsOption::all;
-      row.SetData(null, "All");
+      row.SetData(null, $"All");
    }
 
    bool OptionCheck(ProjectOptions options)
@@ -1123,13 +1261,13 @@ void DrawStipple(Surface surface, Size clientSize)
 
    surface.LineStipple(0x5555);
    surface.Rectangle(x1, y1, x2, y2);
-   surface.LineStipple(0);            
+   surface.LineStipple(0);
 }
 
 class BuildTab : Tab
 {
-   text = "Build";
-   background = activeBorder;
+   text = $"Build";
+   background = formColor;
    tabCycle = true;
 
    ProjectNode backupNode;
@@ -1164,7 +1302,7 @@ class BuildTab : Tab
             if(button && button.id)
             {
                Platform platform = (Platform)button.id;
-               char * platformName = platform ? platform.OnGetString(0,0,0) : null; // all these platformName are leaking, no? 
+               char * platformName = platform ? platform.OnGetString(0,0,0) : null; // all these platformName are leaking, no?
                return platformName;
             }
          }
@@ -1184,7 +1322,7 @@ class BuildTab : Tab
    };
    SelectorBar configSelector
    {
-      this, text = "Configurations: ", anchor = { left = 98, top = 8, right = 54 }; size = { 0, 26 };
+      this, text = $"Configurations: ", anchor = { left = 98, top = 8, right = 54 }; size = { 0, 26 };
       opacity = 0;
       direction = horizontal, scrollable = true;
 
@@ -1202,7 +1340,7 @@ class BuildTab : Tab
          }
          return SelectorBar::OnKeyDown(key, ch);
       }
-      
+
       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
       {
          ((BuildTab)master).labelConfigurations.Update(null);
@@ -1233,7 +1371,7 @@ class BuildTab : Tab
             name = CopyString(tmp);
             options =
             {
-               // objectsDirectory = CopyString(defaultObjDirExpression);
+               // objectsDirectory = /*CopyString(*/defaultObjDirExpression/*)*/;
             };
          };
          if(!project.topNode.configurations) project.topNode.configurations = { };
@@ -1241,17 +1379,17 @@ class BuildTab : Tab
          /*
          targetType = project.config.options.targetType;
          config.options.
-         config.options.targetFileName = CopyString(project.moduleName);
-         config.targetDir.dir = "";
-         config.objectsDirectory = CopyString(defaultObjDirExpression);
+         config.options.targetFileName = project.moduleName;
+         config.options.targetDir.dir = "";
+         config.options.objectsDirectory = defaultObjDirExpression);
          config.options.debug = true;
          config.options.optimization = none;
          config.options.warnings = all;
-         */         
+         */
 
          button =
          {
-            configSelector, renameable = true, master = this, text = config.name, id = (int)config;
+            configSelector, renameable = true, master = this, text = config.name, id = (int64)config;
             NotifyClicked = ConfigClicked, OnRename = ConfigOnRename;
          };
 
@@ -1278,21 +1416,20 @@ class BuildTab : Tab
       {
          if(config)
          {
-            String title = PrintString("Delete ", config.name, " Configuration");
-            String msg = PrintString("Are you sure you wish to delete the ", config.name, " configuration?");
+            String title = PrintString($"Delete ", config.name, $" Configuration");
+            String msg = PrintString($"Are you sure you wish to delete the ", config.name, $" configuration?");
             if(MessageBox { type = okCancel, text = title, contents = msg }.Modal() == ok)
             {
                Iterator<Window> it { configSelector.controls };
                ProjectConfig configToDelete = config;
-
+               /*
                while(it.Next())
                {
                   SelectorButton button = (SelectorButton)it.data;
                   if((ProjectConfig)button.id == config)
-                  {                     
+                  {
                      button.visible = false;
                      button.Destroy(0);
-                     delete button;
 
                      if(it.Prev())
                      {
@@ -1303,6 +1440,11 @@ class BuildTab : Tab
                      break;
                   }
                }
+               */
+               SelectorButton button = configSelector.FindButtonByID((int64)configToDelete);
+               if(button)
+                  configSelector.RemoveButton(button);
+
                project.topNode.DeleteConfig(configToDelete);
 
                modifiedDocument = true;
@@ -1313,7 +1455,7 @@ class BuildTab : Tab
          return true;
       }
    };
-   
+
    Label labelPlatforms
    {
       this, anchor = { left = 8, top = 44 }, labeledWindow = platformSelector;
@@ -1326,7 +1468,7 @@ class BuildTab : Tab
    };
    SelectorBar platformSelector
    {
-      this, text = "Platforms: ", anchor = { left = 64, top = 38, right = 54 }; size = { 0, 26 };
+      this, text = $"Platforms: ", anchor = { left = 64, top = 38, right = 54 }; size = { 0, 26 };
       opacity = 0;
       direction = horizontal, scrollable = true;
 
@@ -1339,7 +1481,7 @@ class BuildTab : Tab
 
    TabControl buildTabControl
    {
-      this, background = activeBorder, anchor = { left = 8, top = 64, right = 8, bottom = 8 };
+      this, background = formColor, anchor = { left = 8, top = 64, right = 8, bottom = 8 };
       curTab = compilerTab;
    };
    CompilerTab compilerTab { this, tabControl = buildTabControl };
@@ -1348,7 +1490,7 @@ class BuildTab : Tab
    Label rightClick
    {
       this, font = { font.faceName, font.size, italic = true }, stayOnTop = true,
-      text = "(Right click or press Ctrl-Del to revert an option to inherited value)", anchor = { top = 72, right = 16 }
+      text = $"(Right click or press Ctrl-Del to revert an option to inherited value)", anchor = { top = 72, right = 16 }
    };
 
    void FindUniqueConfigName(char * baseName, bool startWithNumber, char * output)
@@ -1451,7 +1593,7 @@ class BuildTab : Tab
       }
 
       project.topNode.RenameConfig(config.name, *newName);
-      
+
       modifiedDocument = true;
       return true;
    }
@@ -1472,6 +1614,7 @@ class BuildTab : Tab
          if(!mods)
             buildTabControl.Activate();
 
+         compilerTab.fileList.Update(null);
          if(compilerTab.rightPaneHeader.visible)
             compilerTab.rightPaneHeader.Update(null);
          ((ProjectSettings)master).UpdateDialogTitle();
@@ -1490,7 +1633,7 @@ class BuildTab : Tab
          if(!node) node = project.topNode;
 
          newNodeRes = node.isInResources;
-         
+
          currentNode = node;
          if(!ignoreAsLastSelection)
             lastSelectedNode = node;
@@ -1502,13 +1645,13 @@ class BuildTab : Tab
          }
          else
          {
-            compilerTab.rightPaneHeader.id = (int)node;
+            compilerTab.rightPaneHeader.id = (int64)node;
             compilerTab.rightPaneHeader.Update(null);
             compilerTab.rightPaneHeader.visible = true;
          }
 
          {
-            DataRow row = compilerTab.fileList.FindSubRow((int)currentNode);
+            DataRow row = compilerTab.fileList.FindSubRow((int64)currentNode);
             if(row)
             {
                compilerTab.fileList.currentRow = row;
@@ -1535,10 +1678,11 @@ class BuildTab : Tab
             compilerTab.profiling.visible = !newNodeRes;
             compilerTab.labelOptimization.visible = !newNodeRes;
             compilerTab.optimization.visible = !newNodeRes;
+            compilerTab.fastMath.visible = !newNodeRes;
             compilerTab.labelIncludeDirs.visible = !newNodeRes;
             compilerTab.includeDirs.visible = !newNodeRes;
          }
-         
+
          if(node == project.topNode)
          {
             compilerTab.objDir.visible = true;
@@ -1580,11 +1724,11 @@ class BuildTab : Tab
       // Create Config Buttons
       commonButton = SelectorButton
       {
-         configSelector, master = this, text = "Common", id = (int)null; font = { font.faceName, font.size, true };
+         configSelector, master = this, text = $"Common", id = (int64)null; font = { font.faceName, font.size, true };
          checked = true;
          NotifyClicked = ConfigClicked;
       };
-      
+
       config = null;
 
       if(project.topNode.configurations)
@@ -1593,27 +1737,27 @@ class BuildTab : Tab
          {
             EditableSelectorButton button
             {
-               configSelector, master = this, renameable = true, text = c.name, id = (int)c;
+               configSelector, master = this, renameable = true, text = c.name, id = (int64)c;
                NotifyClicked = ConfigClicked, OnRename = ConfigOnRename;
             };
          }
       }
    }
-   
+
    void Init()
    {
       Platform p;
       SelectorButton button;
 
       activeConfigName = project.config ? CopyString(project.config.name) : null;
-      
+
       compilerTab.AddNode(project.topNode, null);
 
       CreateConfigButtons();
 
       platformButton = button =
       {
-         platformSelector, master = this, text = "Common", id = 0;  font = { font.faceName, font.size, true };
+         platformSelector, master = this, text = $"Common", id = 0;  font = { font.faceName, font.size, true };
          NotifyClicked = PlatformClicked; checked = true;
       };
 
@@ -1623,7 +1767,7 @@ class BuildTab : Tab
       {
          SelectorButton button
          {
-            platformSelector, master = this, text = p.OnGetString(0,0,0), id = (int)p; 
+            platformSelector, master = this, text = p.OnGetString(0,0,0), id = (int64)p;
             NotifyClicked = PlatformClicked;
          };
       }
@@ -1685,6 +1829,14 @@ class BuildTab : Tab
             }
          }
       }
+      if(!project.config)
+      {
+         List<ProjectConfig> configs = project.topNode.configurations;
+         if(configs && configs.count)
+            project.config = configs[0];
+      }
+
+      ide.UpdateToolBarActiveConfigs(false);
    }
 
    void RevertChanges()
@@ -1728,8 +1880,8 @@ class BuildTab : Tab
          DialogResult diagRes = MessageBox
          {
             type = yesNoCancel, master = ide,
-            text = "Save changes to project settings?",
-            contents = "Would you like to save changes made to the build options?"
+            text = $"Save changes to project settings?",
+            contents = $"Would you like to save changes made to the build options?"
          }.Modal();
          if(diagRes == no)
             RevertChanges();
@@ -1740,6 +1892,7 @@ class BuildTab : Tab
             project.MarkChanges(backupNode);
             project.topNode.modified = true;
             ide.projectView.modifiedDocument = true;
+            ide.UpdateToolBarActiveConfigs(false);
             ide.projectView.Update(null);
          }
          modifiedDocument = false;
@@ -1750,10 +1903,10 @@ class BuildTab : Tab
 
 class CompilerTab : Tab
 {
-   background = activeBorder;
-   text = "Compiler";
+   background = formColor;
+   text = $"Compiler";
 
-   Window leftPane { this, size = { 180 }, anchor = { left = 0, top = 0, bottom = 0 }, background = activeBorder };
+   Window leftPane { this, size = { 180 }, anchor = { left = 0, top = 0, bottom = 0 }, background = formColor };
 
    Label labelFileList { leftPane, this, position = { 8, 8 }, labeledWindow = fileList };
    ListBox fileList
@@ -1765,7 +1918,7 @@ class CompilerTab : Tab
       selectionColor = unfocusedSelectorColor;
       size = { 180 };
       anchor = Anchor { left = 8, top = 24, right = 4, bottom = 8 };
-      text = "Files";
+      text = $"Files";
 
       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
       {
@@ -1806,9 +1959,9 @@ class CompilerTab : Tab
       }
    };
 
-   Window rightPane 
+   Window rightPane
    {
-      this, anchor = { left = 196, top = 0, right = 0, bottom = 0 }, background = activeBorder, tabCycle = true;
+      this, anchor = { left = 196, top = 0, right = 0, bottom = 0 }, background = formColor, tabCycle = true;
    };
 
    Window rightPaneHeader
@@ -1821,8 +1974,8 @@ class CompilerTab : Tab
          if(id)
          {
             ide.projectView.drawingInProjectSettingsDialogHeader = true;
-            class(ProjectNode)._vTbl[__ecereVMethodID_class_OnDisplay](class(ProjectNode),
-               id, surface, 8, 2, clientSize.w, ide.projectView, Alignment::left, DataDisplayFlags { selected = true });
+            ((void (*)(void *, void *, void *, int, int, int, void *, uint, uint))(void *)class(ProjectNode)._vTbl[__ecereVMethodID_class_OnDisplay])(class(ProjectNode),
+               (void *)id, surface, 8, 2, clientSize.w, ide.projectView, Alignment::left, DataDisplayFlags { selected = true });
             ide.projectView.drawingInProjectSettingsDialogHeader = false;
          }
       }
@@ -1837,77 +1990,91 @@ class CompilerTab : Tab
    PathOptionBox objDir
    {
       rightPane, this, size = { 250, 22 }, anchor = { left = 8, top = 24, right = 8 };
-      text = "Intermediate Objects Directory", hotKey = altJ, option = OPTION(objectsDirectory);
+      text = $"Intermediate Objects Directory", hotKey = altJ, option = OPTION(objectsDirectory);
    };
 
    BoolOptionBox excludeFromBuild
    {
       rightPane, this, position = { 8, 28 },
-      text = "Exclude from Build", visible = false, option = OPTION(excludeFromBuild);
+      text = $"Exclude from Build", visible = false, option = OPTION(excludeFromBuild);
    };
 
    Label labelPreprocessorDefs { rightPane, this, position = { 8, 50 }, labeledWindow = preprocessorDefs };
    StringArrayOptionBox preprocessorDefs
    {
       rightPane, this, size = { 290, 22 }, anchor = { left = 8, top = 66, right = 8 };
-      text = "Preprocessor Definitions", hotKey = altD, option = OPTION(preprocessorDefinitions);
+      text = $"Preprocessor Definitions", hotKey = altD, option = OPTION(preprocessorDefinitions);
    };
 
    Label labelDefaultNameSpace { rightPane, this, position = { 8, 92 }, labeledWindow = defaultNameSpace };
    StringOptionBox defaultNameSpace
    {
       rightPane, this, size = { 160, 22 }, position = { 8, 108 };
-      text = "Default Name Space", option = OPTION(defaultNameSpace);
+      text = $"Default Name Space", option = OPTION(defaultNameSpace);
    };
    BoolOptionBox strictNameSpaces
    {
-      rightPane, this, position = { 172, 112 }, 
-      text = "Strict Name Spaces", option = OPTION(strictNameSpaces);
+      rightPane, this, position = { 172, 112 },
+      text = $"Strict Name Spaces", option = OPTION(strictNameSpaces);
+   };
+
+   BoolOptionBox fastMath
+   {
+      rightPane, this, position = { 316, 112 },
+      text = $"Fast Math", option = OPTION(fastMath);
    };
 
    BoolOptionBox memoryGuard
    {
       rightPane, this, position = { 8, 154 };
-      text = "MemoryGuard", hotKey = altM, option = OPTION(memoryGuard);
+      text = $"MemoryGuard", hotKey = altM, option = OPTION(memoryGuard);
    };
 
    Label labelWarnings { rightPane, position = { 116, 138 }, labeledWindow = warnings };
    WarningsDB warnings
    {
       rightPane, this, position = { 116, 154 };
-      text = "Warnings", hotKey = altW, option = OPTION(warnings);
+      text = $"Warnings", hotKey = altW, option = OPTION(warnings);
    };
 
-   Label labelOptimization { rightPane, position = { 244, 138 }, labeledWindow = optimization };
+   Label labelOptimization { rightPane, position = { 220, 138 }, labeledWindow = optimization };
    OptimizationDB optimization
    {
-      rightPane, this, position = { 244, 154 }, size = { 120, 22 };
-      text = "Optimization", hotKey = altO, option = OPTION(optimization);
+      rightPane, this, position = { 220, 154 }, size = { 120, 22 };
+      text = $"Optimization", hotKey = altO, option = OPTION(optimization);
    };
 
    BoolOptionBox debug
    {
       rightPane, this, position = { 8, 188 };
-      text = "Debuggable", hotKey = altG, option = OPTION(debug);
+      text = $"Debuggable", hotKey = altG, option = OPTION(debug);
    };
 
    BoolOptionBox profiling
    {
       rightPane, this, position = { 116, 188 };
-      text = "Profiling Data", hotKey = altP, option = OPTION(profile);
+      text = $"Profiling Data", hotKey = altP, option = OPTION(profile);
    };
 
    BoolOptionBox noLineNumbers
    {
-      rightPane, this, position = { 244, 188 };
-      text = "No Line Numbers", hotKey = altN, option = OPTION(noLineNumbers);
+      rightPane, this, position = { 220, 188 };
+      text = $"No Line Numbers", hotKey = altN, option = OPTION(noLineNumbers);
+   };
+
+   Label labelCompilerOptions { rightPane, this, position = { 8, 208 }, labeledWindow = compilerOptions };
+   StringArrayOptionBox compilerOptions
+   {
+      rightPane, this, size = { 290, 22 }, anchor = { left = 8, top = 224, right = 8 };
+      text = $"Compiler Options", hotKey = altO, option = OPTION(compilerOptions);
+      configReplaces = true;
    };
 
    Label labelIncludeDirs { includeDirs.editor, labeledWindow = includeDirs, position = { 0, 6 }; };
    DirsArrayOptionBox includeDirs
    {
-      rightPane, this, size = { 290, 22 }, anchor = { left = 8, top = 208, right = 8, bottom = 8 };
-      text = "Additional Include Directories", hotKey = altI, option = OPTION(includeDirs);
+      rightPane, this, size = { 290, 22 }, anchor = { left = 8, top = 250, right = 8, bottom = 8 };
+      text = $"Additional Include Directories", hotKey = altI, option = OPTION(includeDirs), switchToKeep = "I";
    };
 
    CompilerTab()
@@ -1927,13 +2094,13 @@ class CompilerTab : Tab
    {
       DataRow row = addTo ? addTo.AddRow() : fileList.AddRow();
 
-      row.tag = (int)node;
+      row.tag = (int64)node;
 
       row.SetData(null, node);
 
-      if(node.files && node.files.first && node.parent && 
-            !(!node.parent.parent && 
-               (!strcmpi(node.name, "notes") || !strcmpi(node.name, "sources") || 
+      if(node.files && node.files.first && node.parent &&
+            !(!node.parent.parent &&
+               (!strcmpi(node.name, "notes") || !strcmpi(node.name, "sources") ||
                   !strcmpi(node.name, "src") || !strcmpi(node.name, "tools"))))
          row.collapsed = true;
       else if(node.type == folder)
@@ -1956,7 +2123,7 @@ class CompilerTab : Tab
       if(activeChild && activeChild.active)
       {
          Window control = activeChild;
-         control.Deactivate();         
+         control.Deactivate();
          control.Activate();
       }
    }
@@ -1970,35 +2137,35 @@ class CompilerTab : Tab
 
 class LinkerTab : Tab
 {
-   background = activeBorder;
-   text = "Linker";
+   background = formColor;
+   text = $"Linker";
 
    Label labelTargetName { this, position = { 8, 8 }, labeledWindow = targetName };
    StringOptionBox targetName
    {
       this, position = { 8, 24 }, size = { 200, 22 };
-      text = "Target Name", hotKey = altN, option = OPTION(targetFileName);
+      text = $"Target Name", hotKey = altN, option = OPTION(targetFileName);
    };
-   
+
    Label labelTargetType { this, position = { 216, 8 }, labeledWindow = targetType };
    TargetTypeDB targetType
    {
       this, position = { 216, 24 }, size = { 120, 22 };
-      text = "Target Type", hotKey = altT, option = OPTION(targetType);
+      text = $"Target Type", hotKey = altT, option = OPTION(targetType);
    };
-   
+
    Label labelTargetDirectory { this, position = { 344, 8 }, labeledWindow = targetDirectory };
    PathOptionBox targetDirectory
    {
       this, size = { 270, 22 }, anchor = { left = 344, top = 24, right = 8 };
-      hotKey = altR, text = "Target Directory", option = OPTION(targetDirectory);
+      hotKey = altR, text = $"Target Directory", option = OPTION(targetDirectory);
    };
 
    Label labelLibraries { this, position = { 8, 50 }, labeledWindow = libraries };
    StringArrayOptionBox libraries
    {
       this, size = { 290, 22 }, anchor = { left = 8, top = 66, right = 8 };
-      text = "Additional Libraries", hotKey = altL, option = OPTION(libraries);
+      text = $"Additional Libraries", hotKey = altL, option = OPTION(libraries), switchToKeep = "l";
       configReplaces = true;
    };
 
@@ -2006,27 +2173,27 @@ class LinkerTab : Tab
    StringArrayOptionBox linkerOptions
    {
       this, size = { 290, 22 }, anchor = { left = 8, top = 108, right = 8 };
-      text = "Linker Options", hotKey = altO, option = OPTION(linkerOptions);
+      text = $"Linker Options", hotKey = altO, option = OPTION(linkerOptions);
       configReplaces = true;
    };
 
    BoolOptionBox console
    {
       this, position = { 8, 138 };
-      text = "Console Application", hotKey = altC, option = OPTION(console);
+      text = $"Console Application", hotKey = altC, option = OPTION(console);
    };
 
    BoolOptionBox compress
    {
       this, position = { 8, 162 };
-      text = "Compress", hotKey = altW, option = OPTION(compress);
+      text = $"Compress", hotKey = altW, option = OPTION(compress);
    };
 
    Label labelLibraryDirs { libraryDirs.editor, labeledWindow = libraryDirs, position = { 0, 6 }; };
    DirsArrayOptionBox libraryDirs
    {
       this, size = { 290, 22 }, anchor = { left = 8, top = 182, right = 8, bottom = 8 };
-      text = "Additional Library Directories", hotKey = altY, option = OPTION(libraryDirs);
+      text = $"Additional Library Directories", hotKey = altY, option = OPTION(libraryDirs), switchToKeep = "L";
    };
 
    bool OnCreate()
@@ -2046,7 +2213,7 @@ class LinkerTab : Tab
       if(activeChild && activeChild.active)
       {
          Window control = activeChild;
-         control.Deactivate();         
+         control.Deactivate();
          control.Activate();
       }
    }
@@ -2054,21 +2221,28 @@ class LinkerTab : Tab
 
 class BuilderTab : Tab
 {
-   background = activeBorder;
-   text = "Builder";
+   background = formColor;
+   text = $"Builder";
 
    Label labelPrebuildCommands { prebuildCommands.editor, labeledWindow = prebuildCommands, position = { 0, 6 }; };
    StringsArrayOptionBox prebuildCommands
    {
-      this, size = { 290, 100 }, anchor = { left = 8, top = 52, right = 8 };
-      text = "Pre-build Commands", hotKey = altE, option = OPTION(prebuildCommands);
+      this, size = { 290, 92 }, anchor = { left = 8, top = 8, right = 8, bottom = 200 };
+      text = $"Pre-build Commands", hotKey = altE, option = OPTION(prebuildCommands);
    };
 
    Label labelPostbuildCommands { postbuildCommands.editor, labeledWindow = postbuildCommands, position = { 0, 6 }; };
    StringsArrayOptionBox postbuildCommands
    {
-      this, size = { 290, 100 }, anchor = { left = 8, top = 160, right = 8 };
-      text = "Post-build Commands", hotKey = altT, option = OPTION(postbuildCommands);
+      this, size = { 290, 92 }, anchor = { left = 8, top = 100, right = 8, bottom = 100 };
+      text = $"Post-build Commands", hotKey = altT, option = OPTION(postbuildCommands);
+   };
+
+   Label labelInstallCommands { installCommands.editor, labeledWindow = installCommands, position = { 0, 6 }; };
+   StringsArrayOptionBox installCommands
+   {
+      this, size = { 290, 92 }, anchor = { left = 8, top = 200, right = 8, bottom = 8 };
+      text = $"Install Commands", hotKey = altT, option = OPTION(installCommands);
    };
 
    void LoadSettings()
@@ -2082,8 +2256,16 @@ class BuilderTab : Tab
       if(activeChild && activeChild.active)
       {
          Window control = activeChild;
-         control.Deactivate();         
+         control.Deactivate();
          control.Activate();
       }
    }
+
+   void OnResize(int width, int height)
+   {
+      int h = (height - 8 * 4) / 3;
+      prebuildCommands.anchor = { left = 8, top = 8, right = 8, bottom = h * 2 + 8 * 3 };
+      postbuildCommands.anchor = { left = 8, top = h + 8 * 2, right = 8, bottom = h + 8 * 2 };
+      installCommands.anchor = { left = 8, top = h * 2 + 8 * 3, right = 8, bottom = 8 };
+   }
 }