-- -- Component Methods Browser -- Platform: OpenComputers -- -- Author: Sharidan -- www.sharidan.dk -- -- You may freely use and distribute this script -- as long as the original author information -- is left as is. -- Please give credit where credit is due :) -- local app = { name = "Component Methods Browser", version = "1.1-i", } -- Display color theme local theme = { titleBar = { "black", "lightBlue", }, desktop = { "white", "gray", }, desktopError = { "red", "gray", }, desktopWarning = { "yellow", "gray", }, desktopAccept = { "lime", "gray", }, desktopGray = { "silver", "gray", }, highlight = { "lightBlue", "gray", }, menu = { normal = { "black", "lightBlue", }, highlight = { "black", "orange", }, }, lua = { text = { "white", "gray", }, comment = { "silver", "gray", }, strings = { "yellow", "gray", }, numbers = { "red", "gray", }, operators = { "magenta", "gray", }, delims = { "lime", "gray", }, consts = { "lightblue", "gray", }, optionals = { "silver", "gray", }, functs = { "lightblue", "gray", }, }, } -- Component references local components = require("component") local fs = require("filesystem") local event = require("event") local uni = require("unicode") local keyb = require("keyboard") -- Control flags local refreshComponents = true local function saveTheme() if (not fs.exists("/etc/cmb.theme")) then local f = io.open("/etc/cmb.theme", "w") if (f) then local ser = require("serialization") f:write("-- Theme file for: "..app.name.."\n") f:write(ser.serialize(theme)) f:close() ser = nil end f = nil end end local function loadTheme() if (fs.exists("/etc/cmb.theme")) then local f = io.open("/etc/cmb.theme", "r") if (f) then local reading = true local data = nil while reading do local ln,_ = f:read() if (ln) then if (string.sub(ln, 1, 1) == "{") then data = ln reading = nil end else reading = nil end end f:close() if (data) then local ser = require("serialization") theme = ser.unserialize(data) ser = nil end data = nil end f = nil else saveTheme() end end -- Snippet: term -- GPU wrapper that creates an alternative term object -- for manipulating screen contents. local function newTerm() local this = {} -- this._so = require("component").gpu this._so = components.gpu this._od = this._so.getDepth() this._ow, this._oh = this._so.getResolution() this.width = this._ow this.height = this._oh this.depth = this._od this._cx, this._cy = 1, 1 function this:color(fore, back) local function fixclr(c, d) local cl = { "white", "orange", "magenta", "lightblue", "yellow", "lime", "pink", "gray", "silver", "cyan", "purple", "blue", "brown", "green", "red", "black" } local cn = { 0xffffff, 0xffcc33, 0xcc66cc, 0x6699ff, 0xffff33, 0x33cc33, 0xff6699, 0x333333, 0xcccccc, 0x336699, 0x9933cc, 0x333399, 0x663300, 0x336600, 0xff3333, 0x000000 } local nc = -1 if (type(c) == "string") then local t = string.lower(string.gsub(c, " ", "")) t = string.gsub(t, "grey", "gray") t = string.gsub(t, "lightgray", "silver") for i = 1, #cl do if (t == cl[i]) then nc = i break end end elseif (type(c) == "number") then if (c >= 0 and c <= 15) then nc = c + 1 end end cl = nil if (d == 1 and nc > -1) then -- Gray Cyan Purple Blue Brown Green Black if (nc == 8 or nc == 10 or nc == 11 or nc == 12 or nc == 13 or nc == 14 or nc == 16) then return cn[16] -- Black else return cn[1] -- White end elseif ((d == 4 or d == 8) and nc > -1) then return cn[nc] end return nil end local d = self._so.getDepth() local f, fp = fixclr(fore, d) local b, bp = fixclr(back, d) if (f) then self._so.setForeground(f, false) end if (b) then self._so.setBackground(b, false) end end function this:cls(fore, back) if (fore) then self:color(fore, back) end self._so.fill(1, 1, self.width, self.height, " ") self._cx = 1 self._cy = 1 end function this:clearln(y, fore, back) if (fore) then self:color(fore, back) end self._so.set(1, y, string.rep(" ", self.width)) self._cx = 1 self._cy = y end function this:drawRect(x, y, width, height, ch) self._so.fill(x, y, width, height, ch) self._cx = x self._cy = y end function this:write(text) local txt = tostring(text) self._so.set(self._cx, self._cy, txt) self._cx = self._cx + #txt end function this:writeXY(x, y, text) local txt = tostring(text) self._so.set(x, y, txt) self._cx = x + #txt self._cy = y end function this:rightXY(x, y, text) local txt = tostring(text) self._so.set((x - #txt) + 1, y, txt) self._cx = x + 1 self._cy = y end function this:centerY(y, text) local txt = tostring(text) local x = math.floor((self.width - #txt) / 2) + 1 self._so.set(x, y, txt) self._cx = x + #txt self._cy = y end function this:getXY() return self._cx, self._cy end function this:gotoXY(x, y) self._cx = tonumber(x) self._cy = tonumber(y) end function this:cleartb(fore, back, tbFore, tbBack, appName, appVers) if (fore) then self:cls(fore, back) self:clearln(1, tbFore, tbBack) self:writeXY(2, 1, appName) self:rightXY(self.width - 1, 1, "v"..appVers) else self:cls(table.unpack(theme.desktop)) self:clearln(1, table.unpack(theme.titleBar)) self:writeXY(2, 1, app.name) self:rightXY(self.width - 1, 1, "v"..app.version) end end function this:init(width, height, depth) self:cls("white", "black") self._so.setResolution(width, height) if (type(depth) == "number") then if (depth == 1 or depth == 4 or depth == 8) then self._so.setDepth(depth) end end self.width, self.height = self._so.getResolution() self.depth = self._so.getDepth() end function this:close() self:cls("white", "black") self:drawRect(1, 1, self._ow, self._oh, " ") self._so.setResolution(self._ow, self._oh) self._so.setDepth(self._od) self:cls("white", "black") if (package.loaded.term) then package.loaded.term.setCursor(1, 1) else require("term").setCursor(1, 1) end end return this end local term = newTerm() -- Self contained list object -- Requires: term object !! local function newList(x, y, width, height) local this = {} this._x = 1 this._y = 1 this._w = 0 this._h = 0 this.width = 0 this.height = 0 this._nf = "white" this._nb = "gray" this._hf = "black" this._hb = "orange" this._le = {} this._si = 1 this._pi = 0 this._lo = 0 this._ov = true this._ur = true this._urf = true function this:clear(all) self._le = nil self._le = {} if (all) then self._x = 1 self._y = 1 self._w = 0 self._h = 0 self.width = 0 self.height = 0 self._si = 1 self._lo = 0 self._ur = true self._urf = true end os.sleep(0) end function this:setXY(x, y) if (tonumber(x) and tonumber(y)) then if (self._x ~= x or self._y ~= y) then self._x = x self._y = y self._ur = true self._urf = true end end end function this:getXY() return self._x, self._y end function this:setWidth(width) if (tonumber(width)) then if (self._w ~= width) then self._w, self.width = width, width self._ur = true self._urf = true end end end function this:setHeight(height) if (tonumber(height)) then if (self._h ~= height) then self._h, self.height = height, height self._ur = true self._urf = true end end end function this:setSize(width, height) if (tonumber(width) and tonumber(height)) then if (self._w ~= width or self._h ~= height) then self._w, self.width = width, width self._h, self.height = height, height self._ur = true self._urf = true end end end function this:getSize() return self._w, self._h end function this:setNormal(fore, back) if (type(fore) == "string" and type(back) == "string") then self._nf, self._nb = fore, back end end function this:setHighlight(fore, back) if (type(fore) == "string" and type(back) == "string") then self._hf, self._hb = fore, back end end function this:add(entry) if (entry) then table.insert(self._le, tostring(entry)) self._ur, self._urf = true, true end end function this:setList(list) if (type(list) == "table") then self._le = nil self._le = {} for l = 1, #list do table.insert(self._le, tostring(list[l])) end os.sleep(0) self._ur, self._urf = true, true end end function this:getList() return self._le end function this:getSelectedIndex() if (#self._le > 0) then return self._si else return 0 end end function this:getSelectedEntry() if (#self._le > 0) then return self._le[self._si] else return "" end end function this:refresh() self._ur = true self._urf = true return true end function this:render() if (self._ur) then local spc = true for y = 1, self._h do local i = self._lo + y if (i <= #self._le) then if (i == self._si) then term:color(self._hf, self._hb) spc = true else if ((i == self._pi or self._urf) and spc) then term:color(self._nf, self._nb) spc = false end end if ((i == self._pi or self._urf) or i == self._si) then local le = " "..self._le[i]..string.rep(" ", self._w) if (#le > self._w) then le = string.sub(le, 1, self._w) end term:writeXY(self._x, self._y + (y - 1), le) le = nil end elseif (self._urf) then if (spc) then term:color(self._nf, self._nb) end term:writeXY(self._x, self._y + (y - 1), string.rep(" ", self._w)) end end spc = nil self._urf = false self._ur = false os.sleep(0) end end function this:_mud(key) if (key == 200) then if (self._si > 1) then self._pi = self._si self._si = self._si - 1 if (self._si < self._lo + 1) then self._lo = self._lo - 1 self._urf = true end self._ur = true end elseif (key == 208) then if (self._si + 1 <= #self._le) then self._pi = self._si self._si = self._si + 1 if (self._si - self._lo > self._h) then self._lo = self._lo + 1 self._urf = true end self._ur = true end end end function this:checkEvent(...) local eventID, arg1, arg2, arg3, arg4, arg5 = table.unpack({...}) if (eventID == "key_down" and #self._le > 0) then local key = tonumber(arg3) if (key == 199) then -- Home if (self._si > 1) then self._pi = self._si self._si = 1 if (self._lo > 0) then self._lo = 0 self._urf = true end self._ur = true end return nil elseif (key == 207) then -- End if (self._si < #self._le) then self._si = #self._le self._pi = 0 self._lo = #self._le - self._h if (self._lo < 0) then self._lo = 0 end self._ur = true self._urf = true end return nil elseif (key == 201) then -- PgUp if (self._si > self._lo + 1) then self._pi = self._si self._si = self._lo + 1 self._ur = true elseif (self._si == self._lo + 1) then if (self._lo > 0) then self._lo = self._lo - self._h if (self._lo < 0) then self._lo = 0 end self._si = self._lo + 1 self._ur = true self._urf = true end end return nil elseif (key == 209) then -- PgDn if (self._si < self._lo + self._h) then self._pi = self._si self._si = self._lo + self._h if (self._si > #self._le) then self._si = #self._le end self._ur = true else if (self._si < #self._le) then self._lo = self._lo + self._h if (self._lo > #self._le - self._h) then self._lo = #self._le - self._h end self._si = self._lo + self._h if (self._si > #self._le) then self._si = #self._le end self._pi = 0 self._ur = true self._urf = true end end return nil elseif (key == 200) then -- Up self:_mud(key) return nil elseif (key == 208) then -- Down self:_mud(key) return nil elseif (key == 28) then -- Enter return "listbox_select", arg1, self._si, self._le[self._si], arg4, nil end elseif (eventID == "touch" and #self._le > 0) then local x, y = tonumber(arg2), tonumber(arg3) if ((x >= self._x and x <= (self._x + self._w) - 1) and (y >= self._y and y <= (self._y + self._h) - 1)) then if (arg4 == 0) then local ni = self._lo + ((y - self._y) + 1) if (ni == self._si) then return "listbox_select", arg1, self._si, self._le[self._si], arg5, nil elseif (ni <= #self._le) then self._pi = self._si self._si = ni self._ur = true return nil end end end elseif (eventID == "scroll" and #self._le > 0) then local x, y = tonumber(arg2), tonumber(arg3) if ((x >= self._x and x <= (self._x + self._w) - 1) and (y >= self._y and y <= (self._y + self._h) - 1)) then if (arg4 == 1) then self:_mud(200) return nil elseif (arg4 == -1) then self:_mud(208) return nil end end end return eventID, arg1, arg2, arg3, arg4, arg5 end if (type(x) == "number" and type(y) == "number") then this:setXY(x, y) end if (type(width) == "number" and type(height) == "number") then this:setSize(width, height) end return this end -- ## StatusBar caption functions -- Splits an ampersand highlighted caption into a key/label pair local function fixCaption(caption) local key, keyPos, label, work = 0, 0, "", "" work = string.gsub(caption, "&&", ";amp;") local amp = string.find(work, "&") if (amp) then key = string.sub(work, amp + 1, amp + 1) keyPos = tonumber(amp) label = string.gsub(string.gsub(work, "&", ""), ";amp;", "&") return true, key, keyPos, label end return false, 0, 0, string.gsub(work, ";amp;", "&") end -- Alternative key names for on-screen display local function getKeyName(keyNum) local tbl = { [59] = "F1", [60] = "F2", [61] = "F3", [62] = "F4", [63] = "F5", [64] = "F6", [65] = "F7", [66] = "F8", [67] = "F9", [68] = "F10", [87] = "F11", [88] = "F12", [183] = "PrnScr", [70] = "ScrLck", [197] = "Pause", [41] = "Tilde", [12] = "Minus", [13] = "Equals", [14] = "BckSpc", [210] = "Ins", [199] = "Home", [201] = "PgUp", [69] = "NumLck", [181] = "Pad/", [55] = "Pad*", [74] = "Pad-", [15] = "Tab", [26] = "LBracket", [27] = "RBracket", [43] = "BSlash", [211] = "Del", [207] = "End", [209] = "PgDn", [71] = "Pad7", [72] = "Pad8", [73] = "Pad9", [78] = "Pad+", [58] = "CpsLck", [39] = "SColon", [40] = "Apos", [28] = "Enter", [75] = "Pad4", [76] = "Pad5", [77] = "Pad6", [42] = "LShift", [51] = "Comma", [52] = "Period", [53] = "Slash", [54] = "RShift", [200] = "Up", [79] = "Pad1", [80] = "Pad2", [81] = "Pad3", [29] = "LCtrl", [219] = "LSuper", [56] = "Alt", [57] = "Space", [184] = "AltGr", [220] = "RSuper", [221] = "WMenu", [157] = "RShift", [203] = "Left", [208] = "Down", [205] = "Right", [82] = "Pad0", [83] = "Pad." } local res = tbl[keyNum] or "???" tbl = nil os.sleep(0) return res end -- Snippet: statusBar local function newStatusBar(...) local this = {} this._nt = "" this._ht = {} function this:set(...) local labels = {...} self._nt = "" self._ht = nil self._ht = {} local x, keyCap = 2, "" for l = 1, #labels do if (type(labels[l]) == "number") then keyCap = getKeyName(labels[l]) elseif (type(labels[l]) == "string") then local success, key, keyPos, cap = fixCaption(labels[l]) if (not success) then key = 0 keyPos = 0 end if (#keyCap > 0) then if (term.depth == 1) then self._nt = self._nt.."["..keyCap.."]: "..cap else self._nt = self._nt.."["..string.rep(" ", #keyCap).."]: "..cap table.insert(self._ht, { (x + 1), keyCap } ) end else if (keyPos > 0) then if (term.depth == 1) then self._nt = self._nt.."["..string.upper(key).."]: "..cap else self._nt = self._nt..cap table.insert(self._ht, { (x + (keyPos - 1)), key } ) end else self._nt = self._nt..cap end end end if (type(labels[l]) == "string" and l < #labels) then self._nt = self._nt..", " end x = #self._nt + 2 end -- for l os.sleep(0) end function this:render() if (self._nt ~= "") then term:clearln(term.height, table.unpack(theme.desktop)) term:writeXY(2, term.height, self._nt) if (#self._ht > 0) then term:color(table.unpack(theme.highlight)) for h = 1, #self._ht do term:writeXY(self._ht[h][1], term.height, self._ht[h][2]) end end end end this:set(...) return this end -- ### End of statusBar caption functions -- Text padding; both left and right local function pad(text, size, pre) local result = tostring(text) while #result < size do if (pre) then result = " "..result else result = result.." " end end return result end -- Splitter function local function split(str, pat) local r = {} for s in string.gmatch(str, "[^"..pat.."]+") do table.insert(r, s) end return r end -- Wrap text inside specified width local function wrapText(text, width) local txt = split(text, "\n") local l = {} for t = 1, #txt do local cl = "" local msg = ""..txt[t].."" for w in msg:gmatch("%S+%s*") do if (#cl + #w >= width) then table.insert(l, cl) cl = w else cl = cl..w end end -- for if (cl ~= "") then table.insert(l, cl) end end return l end -- Remove preceeding & trailing spaces local function trim(text) local txt = tostring(text) if (string.sub(txt, 1, 1) == " ") then while (#txt > 0 and string.sub(txt, 1, 1) == " ") do txt = string.sub(txt, 2, #txt) end end if (string.sub(txt, #txt, #txt) == " ") then while (#txt > 0 and string.sub(txt, #txt, #txt) == " ") do txt = string.sub(txt, 1, #txt - 1) end end return txt end -- Some doc's return "int" instead of "number" local function fixVars(v) if (string.find(v, ",")) then local lst = split(v, ",") for l = 1, #lst do if (string.lower(lst[l]) == "int") then lst[l] = "number" end end return table.concat(lst, ",") else if (string.lower(v) == "int") then return "number" end end return v end -- Splits function parameters into browsable table structures local function splitParameters(params) if (params ~= "" and params ~= nil) then local aopt = false if (string.sub(params, 1, 1) == "[" and string.sub(params, #params, #params) == "]") then params = string.sub(params, 2, #params) params = string.sub(params, 1, #params - 1) aopt = true end local result = {} local args = split(params, ",") local an, at, aat, tn, vn, vt = "", "", "", "" local opt, itl, ivl, vo, sr = false, false, false, false, false local tl, vl = {}, {} for a = 1, #args do sr = false;vo = false if (string.find(args[a], "[:]")) then local ap = split(args[a], ":") an = ap[1];at = ap[2];aat = ap[3] or "" else an = args[a];at = "";aat = "" end an = string.gsub(an, " ", "");at = string.gsub(at, " ", "");aat = string.gsub(aat, " ", "") if (string.sub(at, #at, #at) == "[") then at = string.gsub(at, "[[]", "") if (string.find(at, "[?]")) then at = string.gsub(at, "[?]", "");vo = true;sr = true else sr = true end opt = true elseif (string.sub(an, 1, 1) == "[") then an = string.sub(an, 2, #an) sr = true opt = true elseif (string.sub(at, #at, #at) == "]" and opt) then opt = false;at = string.gsub(at, "[]]", "");vo = true;sr = true elseif (string.sub(at, 1, 1) == "{") then tn = an;tl = {};itl = true;at = string.gsub(at, "[{]", "") if (string.find(aat, "[?]")) then aat = string.gsub(aat, "[?]", "") table.insert(tl, { at, aat, true } ) else table.insert(tl, { at, aat, false } ) end elseif (string.find(at, "[{]")) then local tp = split(at, "{") if (tp[1] == "table") then tn = an;tl = {};itl = true if (string.find(aat, "[?]")) then aat = string.gsub(aat, "[?]", "") table.insert(tl, { tp[2], aat, true } ) else table.insert(tl, { tp[2], aat, false } ) end else vn = an;vt = tp[1];vl = {};ivl = true table.insert(vl, tp[2]) end elseif (string.find(an, "[}]")) then local tp = split(an, "}") if (itl) then table.insert(tl, { tp[1], "", false } ) if (tp[2] == "?") then vo = true end sr = true;an = tn;at = "table" elseif (ivl) then table.insert(vl, tp[1]) if (tp[2] == "?") then vo = true end sr = true;an = vn;at = vt end elseif (itl) then if (string.find(at, "[?]")) then at = string.gsub(at, "[?]", "") table.insert(tl, { an, at, true } ) else table.insert(tl, { an, at, false } ) end elseif (ivl) then table.insert(vl, an) else if (string.find(at, "[?]")) then at = string.gsub(at, "[?]", "");vo = true;sr = true elseif (opt) then vo = true;sr = true else sr = true end end if (sr) then if (aopt) then vo = true end if (itl) then table.insert(result, { an, fixVars(at), vo, tl } ) itl = false;tl = {};tn = "" elseif (ivl) then table.insert(result, { an, fixVars(at), vo, vl } ) ivl = false;vl = {};vn = "";vt = "" else table.insert(result, { an, fixVars(at), vo, false } ) end end end return result end return {} end -- Splits returned documentation into browsable table structures local function splitDoc(methodName, methodType, methodDoc) if (methodDoc) then local code, doc, mn, mt, rt, params = "", "", methodName, "", "", "" local ma = {} local rem1, rem2 = string.find(methodDoc, "[--]") if (rem1 and rem2) then code = string.sub(methodDoc, 1, rem1 - 2) doc = string.sub(methodDoc, rem2 + 3, #methodDoc) end if (code ~= "") then local ps1, ps2 = string.find(code, "[(]") local pe1, pe2 = string.find(code, "[)]") if (ps1 and pe1) then mt = string.sub(code, 1, ps1 - 1) params = string.sub(code, ps1 + 1, pe1 - 1) local tmp = string.sub(code, pe1 + 1, #code) local pc1, pc2 = string.find(tmp, "[:]") if (pc1) then rt = fixVars(trim(string.sub(tmp, pc1 + 1, #tmp))) end end end return { mn, mt, splitParameters(params), rt, doc } else if (methodName == "address" and methodType == "string") then return { methodName, methodType, false, false, "The address of this component." } elseif (methodName == "type" and methodType == "string") then return { methodName, methodType, false, false, "Which type this component is." } elseif (methodName == "slot" and methodType == "number") then return { methodName, methodType, false, false, "The slot this component is installed in." } else return { methodName, methodType, false, false, "No further documentation available." } end end return nil end local function findAddress(partial, addressList) if (string.find(partial, "[..]") or string.find(partial, " ")) then if (string.find(partial, "[..]")) then partial = string.sub(partial, 1, string.find(partial, "[..]") - 1) end if (string.find(partial, " ")) then partial = string.sub(partial, 1, string.find(partial, " ") - 1) end end for a = 1, #addressList do if (string.sub(addressList[a], 1, #partial) == partial) then return addressList[a] end end return partial end -- Get a table of filesystem mounts: -- address, label, mode, path local function getMounts(address) local result = {} for proxy, path in fs.mounts() do local label = proxy.getLabel() or "" local mode = proxy.isReadOnly() and "ro" or "rw" if (address) then if (proxy.address == address) then table.insert(result, { proxy.address, label, mode, path } ) end else table.insert(result, { proxy.address, label, mode, path } ) end end return result end -- Get a list of all attached/visible components -- sorted by type, sub-sorted by address local function getComponents() local result = {} local cal, ctl = {}, {} for a, t in components.list() do local found = false for c = 1, #ctl do if (ctl[c] == t) then found = true break end end if (not found) then table.insert(ctl, t) end if (not cal[t]) then cal[t] = {} end table.insert(cal[t], a) end table.sort(ctl) for c = 1, #ctl do local al = {} for a = 1, #cal[ctl[c]] do table.insert(al, cal[ctl[c]][a]) end table.sort(al) table.insert(result, { ctl[c], al } ) al = nil end ctl = nil cal = nil return result end local function showHelp() local statusBar = newStatusBar("&Quit", 14, "Close help screen") term:cleartb() statusBar:render() term:color(table.unpack(theme.desktop)) term:writeXY(2, 3, "The following keys can be used to navigate") term:writeXY(2, 4, "and select on most screens.") term:writeXY(2, 6, "[ArrowKeys]: Move selection.") term:writeXY(2, 7, "[Enter] : Select highlighted entry.") term:writeXY(2, 8, "[BckSpc] : Back to previous menu.") term:writeXY(2, 10, "[M] : Display mount information.") term:writeXY(15, 12, "Only on address screen.") term:writeXY(2, 14, "[Q] : Quit and return to OS.") term:color(table.unpack(theme.desktopError)) term:writeXY(15, 11, "FileSystems only!") local running = true local res while running do local e, p1, p2, p3, p4, p5 = event.pull() if ((e == "key_down" and p3 == 14) or (e == "touch" and p4 == 1)) then running = nil elseif (e == "key_down" and p3 == 16) then res = "quit" running = nil end end statusBar = nil return res end -- Check all the standard events local function stdEvents(e, p1, p2, p3, p4, p5, ct) if (e) then if (e == "key_down") then if (p3 == 14) then return "menu_back" else local c = string.lower(uni.char(p2)) if (p3 == 50 and ct == "filesystem") then -- "m" return "menu_mount" elseif (p3 == 35) then -- "h" if (showHelp() == "quit") then return "quit" else return "ui_refresh" end elseif (p3 == 16) then -- "q" return "quit" end end elseif (e == "touch" and p4 == 1) then return "menu_back" elseif (string.sub(e, 1, 10) == "component_") then -- Force main menu to refresh component list refreshComponents = true end end return nil end local function varName(componentType) local ren = { chest = { "copper", "crystal", "diamond", "dirtchest9000", "gold", "iron", "obsidian", "silver", "blockvacuumchest", "extrautils_filing_cabinet", }, machine = { "tile_blockbuffer_item", "tileentityfluidcrafter", "blockcrafter", "tileentityhardmedrive", "tilechest", "tiledrive", "tileentityfluidfiller", "tileentityvibrationchamberfluid", "alloy_smelter", "blocksagmill", "auto_painter", "blockvat", "blockwirelesschargertileentity", "farming_station", "blocktransceiver", "blockpoweredspawner", "blocksliceandsplice", "blocksoulbinder", "blocktelepadtileentity", "blockattractor", "blockenchanter", "blockexperienceobelisk", "blockinhibitorobelisk", "blockkillerjoe", "blockspawnguard", "blockweatherobelisk", }, loader = { "adv__item__loader", "fluid_loader", "item_loader", }, unloader = { "adv__item__unloader", "fluid_unloader", "item_unloader", }, dispenser = { "cart_dispenser", "train_dispenser", }, export = { "me_exportbus", }, import = { "me_importbus", }, interface = { "me_interface", }, tank = { "tileentitycertustank", "blockreservoirtileentity", }, generator = { "blockcombustiongenerator", "blockzombiegenerator", "stirling_generator", "generatorfurnace", }, solar = { "blocksolarpaneltileentity", "extrautils_generatorsolar", }, power = { "tile_blockcapacitorbank_name", }, monitor = { "tile_blockpowermonitor", "blockinventorypanel", }, barrel = { "mcp_mobius_betterbarrel", }, trashcan = { "tileentitytrashcanenergy", "tileentitytrashcanfluids", }, } local repl = { "openperipheral_", "tile_thermalexpansion_device_", "tile_thermalexpansion_dynamo_", "tile_thermalexpansion_machine_", "tile_thermalexpansion_", "thermalexpansion_", } local repl2 = { "_basic_name", "_name", } local res = string.lower(componentType) for r = 1, #repl do local tmp = string.sub(res, 1, #repl[r]) if (string.sub(res, 1, #repl[r]) == repl[r]) then res = string.sub(res, #tmp + 1, #res) for i = 1, #repl2 do tmp = string.sub(res, (#res - #repl2[i]) + 1, #res) if (tmp == repl2[i]) then res = string.sub(res, 1, (#res - #repl2[i])) break end end return res end end -- Check for renaming for nr, lst in pairs(ren) do for l = 1, #lst do if (res == lst[l]) then return nr end end end return componentType end local function shiftMore(show) term:color(table.unpack(theme.desktop)) if (show) then term:rightXY(term.width + 3, term.height, "[ ]: "..uni.char(0x25bc).." view more "..uni.char(0x25bc)) term:color(table.unpack(theme.highlight)) term:rightXY(term.width - 17, term.height, "Shift") else term:rightXY(term.width, term.height, string.rep(" ", 23)) end end local function clua(ct) term:color(table.unpack(theme.lua[ct])) end -- Menu: Method Details local function menuDoc(componentType, doc) local objName = varName(componentType) if (#objName > 10) then objName = "peripheral" end local mn, mt = " "..doc[1].." ", doc[2] local x, y, methodx, methody, docY, docD = 1, 1, 0, 0, 0, 0 local hit = {} local docLines = wrapText(doc[5], term.width - 2) local running = true local refreshUI, uiFull, ms, sh, cb = true, true, true, false, false local pi, opi = 1, 0 local res local statusBar = newStatusBar(14, "Back") while running do if (refreshUI) then if (uiFull) then hit = {} term:cleartb() statusBar:render() term:color(table.unpack(theme.desktopGray)) term:writeXY(2, 3, componentType) if (doc[3]) then local rts = { "nil" } if (doc[4]) then if (string.find(doc[4], " or ")) then rts = split(string.gsub(doc[4], " or ", "----"), "----") else rts = { doc[4] } end end if (rts[1] == "") then rts[1] = "nil" end if (rts[1] == "nil") then clua("consts") else clua("text") end term:writeXY(2, 5, rts[1]) clua("operators") term:write(" = ") clua("functs") term:writeXY(4, 6, "function") clua("text") methodx, methody = term:getXY() term:write(mn) clua("delims") if (#doc[3] > 0) then term:write("(") x, y = 5, 7 term:gotoXY(x, y) for p = 1, #doc[3] do x, y = term:getXY() if (x + #doc[3][p][1] + 3 > term.width) then x = 5 y = y + 1 term:gotoXY(x, y) end if (doc[3][p][3]) then clua("optionals") else clua("text") end local arg = " "..doc[3][p][1].." " table.insert(hit, { x, y, arg } ) term:write(arg) if (p < #doc[3]) then clua("delims") term:write(",") end end y = y + 1 clua("delims") term:writeXY(4, y, ")") else term:write("()") end x, y = term:getXY() docY = y + 2 else clua("text") term:writeXY(2, 5, mt) clua("operators") term:write(" = ") clua("optionals") term:write(objName) clua("operators") term:write(".") clua("text") term:write(trim(mn)) x, y = term:getXY() docY = y + 2 end uiFull = false end -- uiFull docD = term.height - docY if (opi > 0) then if (doc[3][opi][3]) then clua("optionals") else clua("text") end term:writeXY(hit[opi][1], hit[opi][2], hit[opi][3]) opi = 0 end if (ms) then -- Method mode if (type(doc[3]) == "table") then if (#doc[3] > 0) then term:color(table.unpack(theme.menu.highlight)) term:writeXY(methodx, methody, mn) end end term:color(table.unpack(theme.desktop)) term:drawRect(1, docY, term.width, term.height - docY, " ") if (#docLines > docD) then if (keyb.isShiftDown()) then term:drawRect(1, 3, term.width, term.height - 3, " ") for d = 1, #docLines do if (d + 2 < term.height) then term:writeXY(2, d + 2, docLines[d]) end end shiftMore(false) else for d = 1, docD do term:writeXY(2, (docY + d) - 1, docLines[d]) end shiftMore(true) end else for d = 1, #docLines do term:writeXY(2, (docY + d) - 1, docLines[d]) end end elseif (doc[3] and pi > 0) then -- Parameter mode term:color(table.unpack(theme.menu.highlight)) term:writeXY(hit[pi][1], hit[pi][2], hit[pi][3]) term:color(table.unpack(theme.desktop)) term:drawRect(1, docY, term.width, term.height - docY, " ") if (doc[3][pi][3]) then clua("optionals") term:writeXY(2, docY, "Parameter "..pi.." (optional):") else clua("text") term:writeXY(2, docY, "Parameter "..pi..":") end term:writeXY(2, docY + 2, doc[3][pi][1]) clua("operators") term:write(" : ") if (doc[3][pi][3]) then clua("optionals") else clua("text") end term:write(doc[3][pi][2]) if (doc[3][pi][4]) then if (doc[3][pi][2] == "table") then local mtn, mtt = 0, 0 local startY = docY + 1 for t = 1, #doc[3][pi][4] do if (#doc[3][pi][4][t][1] > mtn) then mtn = #doc[3][pi][4][t][1] end if (#doc[3][pi][4][t][2] > mtt) then mtt = #doc[3][pi][4][t][2] end end local col1 = 50 - (mtn + mtt + 4) local sep = col1 + mtn + 1 local col2 = sep + 2 -- Render the contents... clua("text") term:writeXY(col1, startY - 1, doc[3][pi][1]) clua("operators") term:write(" = ") clua("delims") term:write("{") for t = 1, #doc[3][pi][4] do if (doc[3][pi][4][t][3]) then clua("optionals") term:writeXY(col1 - 1, (startY + t) - 1, "("..doc[3][pi][4][t][1]) else clua("text") term:writeXY(col1, (startY + t) - 1, doc[3][pi][4][t][1]) end if (doc[3][pi][4][t][2] ~= "") then clua("operators") term:writeXY(sep, (startY + t) - 1, ":") if (doc[3][pi][4][t][3]) then clua("optionals") term:writeXY(col2, (startY + t) - 1, doc[3][pi][4][t][2]..")") else clua("text") term:writeXY(col2, (startY + t) - 1, doc[3][pi][4][t][2]) end else if (doc[3][pi][4][t][3]) then term:write(")") end end end clua("delims") term:rightXY(col1, startY + #doc[3][pi][4], "}") else term:color(table.unpack(theme.desktop)) term:writeXY(2, docY + 4, "Values: ") for v = 1, #doc[3][pi][4] do local txt = doc[3][pi][4][v] local vx, vy = term:getXY() if (vx + #txt + 2 > term.width) then term:gotoXY(10, vy + 1) end term:write(txt) if (v < #doc[3][pi][4]) then term:write(", ") end end end end end refreshUI = false end local e, p1, p2, p3, p4, p5 = event.pull() local r = stdEvents(e, p1, p2, p3, p4, p5) if (r == "menu_back") then running = nil elseif (r == "ui_refresh") then uiFull = true refreshUI = true elseif (r == "quit") then res = "quit" running = nil elseif (e) then if (e == "key_down") then if (ms) then if (#docLines > docD) then if (keyb.isShiftDown() and not sh) then cb = cur.en cur.en = false sh = true refreshUI = true elseif (not keyb.isShiftDown() and sh) then sh = false uiFull = true refreshUI = true cur.en = cb end end if (doc[3] and p3 == 208) then -- arrow down: switch to parameter mode if (#doc[3] > 0) then pi = 1 ms = false clua("text") term:writeXY(methodx, methody, mn) if (#docLines > docD) then shiftMore(false) end refreshUI = true end end else -- if ms if (doc[3] and p3 == 200) then -- arrow up: switch to method mode opi = pi pi = 1 ms = true if (#docLines > docD) then shiftMore(true) end refreshUI = true elseif (doc[3] and p3 == 203) then -- previous parameter if (pi > 1) then opi = pi pi = pi - 1 refreshUI = true end elseif (doc[3] and p3 == 205) then -- next parameter if (pi < #doc[3]) then opi = pi pi = pi + 1 refreshUI = true end end end -- if ms elseif (e == "touch") then if (p4 == 0) then -- left local tx, ty = p2, p3 if ((tx >= methodx and tx <= methodx + (#mn - 1)) and (ty == methody)) then if (not ms) then opi = pi pi = 1 ms = true if (#docLines > docD) then shiftMore(true) end refreshUI = true end else -- Loop through any argument hits for h = 1, #hit do if ((tx >= hit[h][1] and tx <= hit[h][1] + (#hit[h][3] - 1)) and (ty == hit[h][2])) then if (ms) then pi = h ms = false clua("text") term:writeXY(methodx, methody, mn) if (#docLines > docD) then shiftMore(false) end refreshUI = true break else opi = pi pi = h refreshUI = true break end end end end end end end end statusBar = nil os.sleep(0) return res end -- Menu: Component Methods List local function menuMethods(componentType, address) local running = true local refreshUI = true local docList = {} local statusBar = newStatusBar(14, "Back", 28, "View") local list = newList(1, 4, term.width, term.height - 5) local dev, reason = components.proxy(address) if (dev) then local lst = {} for n, v in pairs(dev) do table.insert(lst, n) end table.sort(lst) for l = 1, #lst do table.insert(docList, splitDoc(lst[l], type(dev[lst[l]]), components.doc(address, lst[l]))) end lst = nil for d = 1, #docList do list:add(docList[d][1]) end end local res while running do if (refreshUI) then term:cleartb() statusBar:render() if (dev) then term:color(table.unpack(theme.desktop)) term:centerY(2, componentType) else term:color(table.unpack(theme.desktopError)) term:centerY(3, "Component error!") term:color(table.unpack(theme.desktop)) term:writeXY(2, 5, componentType) term:writeXY(2, 6, address) if (reason) then term:writeXY(2, 8, reason) else term:writeXY(2, 8, "Unable to connect to component") end end refreshUI = false end list:render() local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull()) if (e == "listbox_select") then if (menuDoc(componentType, docList[p2]) == "quit") then res = "quit" running = nil else refreshUI = list:refresh() end elseif (e) then r = stdEvents(e, p1, p2, p3, p4, p5) if (r == "menu_back") then running = nil elseif (r == "ui_refresh") then refreshUI = list:refresh() elseif (r == "quit") then res = "quit" running = nil end end end dev = nil list:clear(true) list = nil statusBar = nil os.sleep(0) return res end -- Menu: FileSystem Mounts local function menuMounts(address) local running = true local refreshUI = true local mounts = getMounts(address) local statusBar = newStatusBar("&Quit", 14, "Back") while running do if (refreshUI) then term:cleartb() statusBar:render() term:color(table.unpack(theme.desktop)) term:writeXY(2, 3, "Filesystem on:") term:writeXY(2, 4, address) local y = 6 for m = 1, #mounts do term:color(table.unpack(theme.desktop)) term:writeXY(2, y + 0, "Label:") term:writeXY(2, y + 1, "Mode :") term:writeXY(2, y + 2, "Path :") if (mounts[m][2] == "") then term:color(table.unpack(theme.desktopWarning)) term:writeXY(9, y + 0, "{No label set}") else term:writeXY(9, y + 0, mounts[m][2]) end if (mounts[m][3] == "ro") then term:color(table.unpack(theme.desktopError)) term:writeXY(9, y + 1, "[ro] Read Only") else term:color(table.unpack(theme.desktopAccept)) term:writeXY(9, y + 1, "[rw] Read Write") end term:color(table.unpack(theme.desktop)) term:writeXY(9, y + 2, mounts[m][4]) y = y + 4 end refreshUI = false end local r = stdEvents(event.pull()) if (r == "menu_back") then running = nil elseif (r == "ui_refresh") then refreshUI = true elseif (r == "quit") then return "quit" end end statusBar = nil os.sleep(0) end -- Menu: Component address selection local function menuAddress(componentType, addressList) local running = true local refreshUI = true local statusBar = nil if (componentType == "filesystem") then statusBar = newStatusBar("&Mount" , 14, "Back", 28, "View") else statusBar = newStatusBar(14, "Back", 28, "View") end local list = newList(1, 3, term.width, term.height - 4) if (componentType == "filesystem") then local mounts = getMounts() local address, label, mode, path local mwl, mwp = 0, 0 for a = 1, #addressList do local count = 0 for m = 1, #mounts do if (#mounts[m][2] > mwl) then mwl = #mounts[m][2] end if (#mounts[m][4] > mwp) then mwp = #mounts[m][4] end if (mounts[m][1] == addressList[a]) then address, label, mode, path = mounts[m][1], mounts[m][2], mounts[m][3], mounts[m][4] count = count + 1 end end -- spacing + address + spacing + "[??] " + label + spacing + path + spacing if (term.width >= 1 + #addressList[1] + 1 + 5 + mwl + 1 + mwp + 1) then if (count > 1) then list:add(addressList[a].." "..pad(tostring(count), 4, true).." mounts") else list:add(addressList[a].." ["..mode.."] "..pad(label, mwl).." "..path) end else local diff = term.width - (mwl + mwp + 11) if (diff < 5) then diff = 5 end if (count > 1) then list:add(string.sub(addressList[a], 1, diff)..".. "..pad(tostring(count), 4, true).." mounts") else list:add(string.sub(addressList[a], 1, diff)..".. ["..mode.."] "..pad(label, mwl).." "..path) end end end else local cap = componentType if (#cap > #addressList[1]) then local parts = split(cap, "_") local s = #parts cap = "" while #cap < #addressList[1] do cap = parts[1].."_..." for p = s, #parts do cap = cap.."_"..parts[p] end s = s - 1 end s = s + 2 cap = parts[1].."_..." for p = s, #parts do cap = cap.."_"..parts[p] end end for a = 1, #addressList do if (term.width > 1 + #addressList[1] + 1 + #cap + 1) then list:add(addressList[a].." "..cap) else local diff = term.width - (#cap + 5) if (diff < 5) then diff = 5 end list:add(string.sub(addressList[a], 1, diff)..".. "..cap) end end end local res while running do if (refreshUI) then term:cleartb() statusBar:render() refreshUI = false end list:render() local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull()) if (e == "listbox_select") then local addr = findAddress(p3, addressList) if (menuMethods(componentType, addr) == "quit") then res = "quit" running = nil else refreshUI = list:refresh() end elseif (e) then r = stdEvents(e, p1, p2, p3, p4, p5, componentType) if (r == "menu_back") then running = nil elseif (r == "menu_mount") then local addr = findAddress(list:getSelectedEntry(), addressList) if (menuMounts(addr, addressList) == "quit") then res = "quit" running = nil else refreshUI = list:refresh() end elseif (r == "ui_refresh") then refreshUI = list:refresh() elseif (r == "quit") then res = "quit" running = nil end end end -- while list:clear(true) list = nil statusBar = nil os.sleep(0) return res end -- Menu: Component type selection local function menuType() local running = true local refreshUI = true local cl = {} local ci = 1 local statusBar = newStatusBar("&Quit", "&Help", 28, "View") local list = newList(1, 3, term.width, term.height - 4) while running do if (refreshComponents) then cl = getComponents() list:clear() for c = 1, #cl do list:add(cl[c][1]) end refreshComponents = false refreshUI = true end if (refreshUI) then term:cleartb() statusBar:render() refreshUI = false end list:render() local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull()) if (e == "listbox_select") then for c = 1, #cl do if (cl[c][1] == p3) then if (menuAddress(cl[c][1], cl[c][2]) == "quit") then running = nil else refreshUI = list:refresh() end break end end elseif (e) then local r = stdEvents(e, p1, p2, p3, p4, p5) if (r == "ui_refresh") then refreshUI = list:refresh() elseif (r == "quit") then running = nil end end end list:clear(true) list = nil statusBar = nil os.sleep(0) end loadTheme() if (term.width > 80 and term.height > 25) then term:init(80, 25) end menuType() -- Clean up globals term:close() term = nil -- clear: newTerm() object, not the term API!! theme = nil -- Clear library vars components = nil event = nil keyb = nil uni = nil fs = nil