local frame local basefolder = "ExpressionGate" local function SetStatus(str) if !frame.Status then return end str = str or "" frame.Status:SetText(str) end local function UpFolder(folder) local orig = folder local last = string.sub(folder, -1) if last == "/" || last == "\\" then folder = string.sub(folder, 1, string.len(folder) - 1) end for i = string.len(folder), 1, -1 do local char = string.sub(folder, i, i) if char == "/" || char == "\\" then return string.sub(folder, 1, i - 1) end end return "" end local function Browse(folder) if !frame.Browser then return end // Allows us to refresh, and prevents us from errors. if !folder then folder = frame.Browser.Folder end if !folder then return end frame.Browser.Folder = folder frame.Browser:Clear() if folder != "" then folder = folder .. "/" end local path = basefolder .. "/" .. folder local found = file.Find(path .. "*") // Sort them into alphabetical order, with folders first. table.sort(found, function(a, b) if file.IsDir(path .. a) && !file.IsDir(path .. b) then return true elseif file.IsDir(path .. b) && !file.IsDir(path .. a) then return false end local t = {a, b} table.sort(t) if t[1] == a then return true end return false end) local up = frame.Browser:AddItem("../") up.DoClick = function() RunConsoleCommand("expresseditor_browse", UpFolder(folder)) end for _, filename in ipairs(found) do if file.IsDir(path .. filename) then local item = frame.Browser:AddItem(filename .. "/") item.DoClick = function() RunConsoleCommand("expresseditor_browse", folder .. filename) end else local item = frame.Browser:AddItem(filename) item.DoClick = function() RunConsoleCommand("expresseditor_target", folder .. filename) end end end end concommand.Add("expresseditor_browse", function(_, _, args) Browse(args[1]) end) local function Target(_, _, args) frame.BrowserTarget:SetText(args[1] or "") end concommand.Add("expresseditor_target", Target) local function GetTarget() local target = frame.BrowserTarget:GetValue() if string.sub(target, -4) != ".txt" then target = target .. ".txt" end return basefolder .. "/" .. target end local function Open() local path = GetTarget() if !file.Exists(path) then SetStatus("File not found.") return end local text = file.Read(path) if !text then SetStatus("File could not be read.") return end // Load basic info. local set = {["N@"] = "Name"; ["I@"] = "Inputs"; ["O@"] ="Outputs";} local finished = "" for i, line in pairs(string.Explode("\n", text)) do local found = false for symbol, textbox in pairs(set) do if string.sub(line, 1, string.len(symbol)) == symbol && frame[textbox] then frame[textbox]:SetText(string.sub(line, string.len(symbol) + 1)) set[symbol] = nil found = true end end if !found then finished = finished .. line .. "\n" end end finished = string.sub(finished, 1, string.len(finished) - 1) frame.Editor:SetText(finished) frame.Editor:SetEditable(true) SetStatus("Loaded '" .. path .. "' successfully.") end concommand.Add("expresseditor_open", Open) local function Save() local path = GetTarget() local text = frame.Editor:GetValue() if !text or text == "" then SetStatus("No text to save.") return end file.Write(path, text) SetStatus("Successfully saved '" .. path .. "'.") Browse() // Refresh. end concommand.Add("expresseditor_save", Save) local delete local function Delete() local path = GetTarget() if !file.Exists(path) then SetStatus("File not found.") delete = nil return end if !delete || delete != path then delete = path SetStatus("Are you sure you to delete '" .. path .. "'?") return end file.Delete(path) SetStatus("Successfully deleted '" .. path .. "'.") delete = nil Browse() // Refresh. end concommand.Add("expresseditor_delete", Delete) local function Validate() local text = frame.Editor:GetValue() or "" local parser = WireGateExpressionParser:New(text, frame.Inputs:GetValue() or "", frame.Outputs:GetValue() or "") local err = parser:GetError() if err then SetStatus("Error: " .. err ) for num in string.gmatch(err, "at line (%w)") do local pos = 0 for i, line in pairs(string.Explode("\n", text)) do if i == tonumber(num) then frame.Editor:SetCaretPos(pos) break end pos = pos + string.len(line) + 1 end break end else SetStatus("Successfully validated!") end end concommand.Add("expresseditor_validate", Validate) local loading = false timer.Remove("ExpressEditor.LoadUp") local function LoadUp() if !loading then local name = frame.Name:GetValue() or "" local inputs = frame.Inputs:GetValue() or "" local outputs = frame.Outputs:GetValue() or "" // Remove the extension from the filename. local filename = frame.BrowserTarget:GetValue() or "" if string.sub(filename, -4) == ".txt" then filename = string.sub(filename, 1, string.len(filename) - 4) end RunConsoleCommand("wire_gate_expression_label", name) RunConsoleCommand("wire_gate_expression_inputs", inputs) RunConsoleCommand("wire_gate_expression_outputs", outputs) RunConsoleCommand("wire_gate_expression_filename", filename) local text = frame.Editor:GetValue() or "" local lines = string.Explode("\n", text) local i = 1 timer.Create("ExpressEditor.LoadUp", 0.2, 0, function() if i >= 60 then timer.Remove("ExpressEditor.LoadUp") SetStatus("Loading complete.") else // Loop through any lines the same. while string.Trim(lines[i] or "") == string.Trim(GetConVarString("wire_gate_expression_line" .. i)) && i < 60 do i = i + 1 end RunConsoleCommand("wire_gate_expression_line" .. i, lines[i] or "") SetStatus("Loading " .. math.floor(math.min(i/#lines, 1)*100) .. "%") i = i + 1 end end) else timer.Remove("ExpressEditor.LoadUp") SetStatus("Loading cancelled.") end end concommand.Add("expresseditor_loadtool", LoadUp) local function Spacer(h) local s = vgui.Create("DPanel") s:SetTall(h) s:SetPaintBackground(false) return s end local function Initialize() if frame then frame:SetVisible(true) return end frame = vgui.Create("DFrame") frame:SetTitle("ExpressEditor - Expression Gate Editor by RabidToaster") frame:SetVisible(false) frame:SetDeleteOnClose(false) frame:MakePopup() local w, h = 800, 600 frame:SetSize(w, h) frame:Center() local divider = vgui.Create("DHorizontalDivider", frame) divider:SetPos(10, 30) divider:SetSize(w - 20, h - 40) divider:SetLeftWidth(w - 200) local leftSide = vgui.Create("DPanelList", divider) divider:SetLeft(leftSide) local editor = vgui.Create("DTextEntry", frame) editor:SetPos(10, 30) editor:SetSize(w - 200, h - 60) editor:SetMultiline(true) frame.Editor = editor leftSide:AddItem(editor) local status = vgui.Create("DTextEntry", frame) frame.Status = status leftSide:AddItem(status) status:SetEditable(false) local controls = vgui.Create("DVerticalDivider", frame) divider:SetRight(controls) // File browser. local browser = vgui.Create("DComboBox", controls) browser:SetTall(150) browser:SetMultiple(false) frame.Browser = browser Browse("") controls:SetTop(browser) // Bottom set of controls. local botControls = vgui.Create("DPanelList", controls) botControls:EnableVerticalScrollbar() controls:SetBottom(botControls) local target = vgui.Create("DTextEntry", botControls) frame.BrowserTarget = target botControls:AddItem(target) botControls:AddItem(Spacer(10)) // Name, inputs, outputs. local name = vgui.Create("DTextEntry", botControls) name:SetText("Name") frame.Name = name botControls:AddItem(name) local inputs = vgui.Create("DTextEntry", botControls) inputs:SetText("Inputs") frame.Inputs = inputs botControls:AddItem(inputs) local outputs = vgui.Create("DTextEntry", botControls) outputs:SetText("Outputs") frame.Outputs = outputs botControls:AddItem(outputs) botControls:AddItem(Spacer(10)) // Open file. local open = vgui.Create("DButton", botControls) open:SetText("Open") open:SetConsoleCommand("expresseditor_open") botControls:AddItem(open) // Save file. local save = vgui.Create("DButton", botControls) save:SetText("Save") save:SetConsoleCommand("expresseditor_save") botControls:AddItem(save) // Delete file. local delete = vgui.Create("DButton", botControls) delete:SetText("Delete") delete:SetConsoleCommand("expresseditor_delete") botControls:AddItem(delete) botControls:AddItem(Spacer(10)) // Validate. local validate = vgui.Create("DButton", botControls) validate:SetText("Validate") validate:SetConsoleCommand("expresseditor_validate") botControls:AddItem(validate) // Load into tool... local loadUp = vgui.Create("DButton", botControls) loadUp:SetText("Load Into Tool") loadUp:SetConsoleCommand("expresseditor_loadtool") botControls:AddItem(loadUp) end hook.Add("Initialize", "ExpressEditor", Initialize) if derma then Initialize() end local function Open() if !frame then return end frame:SetVisible(true) end concommand.Add("expresseditor", Open)