Advertisement
Sharidan

(oc)cmb.lua

Jan 25th, 2016
1,811
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 51.98 KB | None | 0 0
  1. --
  2. -- Component Methods Browser
  3. -- Platform: OpenComputers
  4. --
  5. -- Author: Sharidan
  6. --     www.sharidan.dk
  7. --
  8. -- You may freely use and distribute this script
  9. -- as long as the original author information
  10. -- is left as is.
  11. -- Please give credit where credit is due :)
  12. --
  13. local app = {
  14.   name    = "Component Methods Browser",
  15.   version = "1.1-i",
  16. }
  17.  
  18. -- Display color theme
  19. local theme = {
  20.   titleBar = {
  21.     "black", "lightBlue",
  22.   },
  23.   desktop = {
  24.     "white", "gray",
  25.   },
  26.   desktopError = {
  27.     "red", "gray",
  28.   },
  29.   desktopWarning = {
  30.     "yellow", "gray",
  31.   },
  32.   desktopAccept = {
  33.     "lime", "gray",
  34.   },
  35.   desktopGray = {
  36.     "silver", "gray",
  37.   },
  38.   highlight = {
  39.     "lightBlue", "gray",
  40.   },
  41.   menu = {
  42.     normal = {
  43.       "black", "lightBlue",
  44.     },
  45.     highlight = {
  46.       "black", "orange",
  47.     },
  48.   },
  49.   lua = {
  50.     text = {
  51.       "white", "gray",
  52.     },
  53.     comment = {
  54.       "silver", "gray",
  55.     },
  56.     strings = {
  57.       "yellow", "gray",
  58.     },
  59.     numbers = {
  60.       "red", "gray",
  61.     },
  62.     operators = {
  63.       "magenta", "gray",
  64.     },
  65.     delims = {
  66.       "lime", "gray",
  67.     },
  68.     consts = {
  69.       "lightblue", "gray",
  70.     },
  71.     optionals = {
  72.       "silver", "gray",
  73.     },
  74.     functs = {
  75.       "lightblue", "gray",
  76.     },
  77.   },
  78. }
  79.  
  80. -- Component references
  81. local components = require("component")
  82. local fs = require("filesystem")
  83. local event = require("event")
  84. local uni = require("unicode")
  85. local keyb = require("keyboard")
  86.  
  87. -- Control flags
  88. local refreshComponents = true
  89.  
  90. local function saveTheme()
  91.   if (not fs.exists("/etc/cmb.theme")) then
  92.     local f = io.open("/etc/cmb.theme", "w")
  93.     if (f) then
  94.       local ser = require("serialization")
  95.       f:write("-- Theme file for: "..app.name.."\n")
  96.       f:write(ser.serialize(theme))
  97.       f:close()
  98.       ser = nil
  99.     end
  100.     f = nil
  101.   end
  102. end
  103.  
  104. local function loadTheme()
  105.   if (fs.exists("/etc/cmb.theme")) then
  106.     local f = io.open("/etc/cmb.theme", "r")
  107.     if (f) then
  108.       local reading = true
  109.       local data  = nil
  110.       while reading do
  111.         local ln,_ = f:read()
  112.         if (ln) then
  113.           if (string.sub(ln, 1, 1) == "{") then
  114.             data = ln
  115.             reading = nil
  116.           end
  117.         else
  118.           reading = nil
  119.         end
  120.       end
  121.       f:close()
  122.       if (data) then
  123.         local ser = require("serialization")
  124.         theme = ser.unserialize(data)
  125.         ser = nil
  126.       end
  127.       data = nil
  128.     end
  129.     f = nil
  130.   else
  131.     saveTheme()
  132.   end
  133. end
  134.  
  135. -- Snippet: term
  136. -- GPU wrapper that creates an alternative term object
  137. -- for manipulating screen contents.
  138. local function newTerm()
  139.   local this = {}
  140.  
  141.   -- this._so = require("component").gpu
  142.   this._so = components.gpu
  143.   this._od = this._so.getDepth()
  144.   this._ow, this._oh = this._so.getResolution()
  145.   this.width = this._ow
  146.   this.height = this._oh
  147.   this.depth = this._od
  148.   this._cx, this._cy = 1, 1
  149.  
  150.   function this:color(fore, back)
  151.     local function fixclr(c, d)
  152.       local cl = { "white", "orange", "magenta", "lightblue", "yellow", "lime", "pink", "gray", "silver", "cyan", "purple", "blue", "brown", "green", "red", "black" }
  153.       local cn = { 0xffffff, 0xffcc33, 0xcc66cc, 0x6699ff, 0xffff33, 0x33cc33, 0xff6699, 0x333333, 0xcccccc, 0x336699, 0x9933cc, 0x333399, 0x663300, 0x336600, 0xff3333, 0x000000 }
  154.       local nc = -1
  155.       if (type(c) == "string") then
  156.         local t = string.lower(string.gsub(c, " ", ""))
  157.         t = string.gsub(t, "grey", "gray")
  158.         t = string.gsub(t, "lightgray", "silver")
  159.         for i = 1, #cl do
  160.           if (t == cl[i]) then
  161.             nc = i
  162.             break
  163.           end
  164.         end
  165.       elseif (type(c) == "number") then
  166.         if (c >= 0 and c <= 15) then
  167.           nc = c + 1
  168.         end
  169.       end
  170.       cl = nil
  171.       if (d == 1 and nc > -1) then
  172.         --     Gray        Cyan      Purple        Blue       Brown       Green       Black
  173.         if (nc == 8 or nc == 10 or nc == 11 or nc == 12 or nc == 13 or nc == 14 or nc == 16) then
  174.           return cn[16] -- Black
  175.         else
  176.           return cn[1] -- White
  177.         end
  178.       elseif ((d == 4 or d == 8) and nc > -1) then
  179.         return cn[nc]
  180.       end
  181.       return nil
  182.     end
  183.     local d = self._so.getDepth()
  184.     local f, fp = fixclr(fore, d)
  185.     local b, bp = fixclr(back, d)
  186.     if (f) then
  187.       self._so.setForeground(f, false)
  188.     end
  189.     if (b) then
  190.       self._so.setBackground(b, false)
  191.     end
  192.   end
  193.   function this:cls(fore, back)
  194.     if (fore) then
  195.       self:color(fore, back)
  196.     end
  197.     self._so.fill(1, 1, self.width, self.height, " ")
  198.     self._cx = 1
  199.     self._cy = 1
  200.   end
  201.   function this:clearln(y, fore, back)
  202.     if (fore) then
  203.       self:color(fore, back)
  204.     end
  205.     self._so.set(1, y, string.rep(" ", self.width))
  206.     self._cx = 1
  207.     self._cy = y
  208.   end
  209.   function this:drawRect(x, y, width, height, ch)
  210.     self._so.fill(x, y, width, height, ch)
  211.     self._cx = x
  212.     self._cy = y
  213.   end
  214.   function this:write(text)
  215.     local txt = tostring(text)
  216.     self._so.set(self._cx, self._cy, txt)
  217.     self._cx = self._cx + #txt
  218.   end
  219.   function this:writeXY(x, y, text)
  220.     local txt = tostring(text)
  221.     self._so.set(x, y, txt)
  222.     self._cx = x + #txt
  223.     self._cy = y
  224.   end
  225.   function this:rightXY(x, y, text)
  226.     local txt = tostring(text)
  227.     self._so.set((x - #txt) + 1, y, txt)
  228.     self._cx = x + 1
  229.     self._cy = y
  230.   end
  231.   function this:centerY(y, text)
  232.     local txt = tostring(text)
  233.     local x = math.floor((self.width - #txt) / 2) + 1
  234.     self._so.set(x, y, txt)
  235.     self._cx = x + #txt
  236.     self._cy = y
  237.   end
  238.   function this:getXY()
  239.     return self._cx, self._cy
  240.   end
  241.   function this:gotoXY(x, y)
  242.     self._cx = tonumber(x)
  243.     self._cy = tonumber(y)
  244.   end
  245.   function this:cleartb(fore, back, tbFore, tbBack, appName, appVers)
  246.     if (fore) then
  247.       self:cls(fore, back)
  248.       self:clearln(1, tbFore, tbBack)
  249.       self:writeXY(2, 1, appName)
  250.       self:rightXY(self.width - 1, 1, "v"..appVers)
  251.     else
  252.       self:cls(table.unpack(theme.desktop))
  253.       self:clearln(1, table.unpack(theme.titleBar))
  254.       self:writeXY(2, 1, app.name)
  255.       self:rightXY(self.width - 1, 1, "v"..app.version)
  256.     end
  257.   end
  258.   function this:init(width, height, depth)
  259.     self:cls("white", "black")
  260.     self._so.setResolution(width, height)
  261.     if (type(depth) == "number") then
  262.       if (depth == 1 or depth == 4 or depth == 8) then
  263.         self._so.setDepth(depth)
  264.       end
  265.     end
  266.     self.width, self.height = self._so.getResolution()
  267.     self.depth = self._so.getDepth()
  268.   end
  269.   function this:close()
  270.     self:cls("white", "black")
  271.     self:drawRect(1, 1, self._ow, self._oh, " ")
  272.     self._so.setResolution(self._ow, self._oh)
  273.     self._so.setDepth(self._od)
  274.     self:cls("white", "black")
  275.     if (package.loaded.term) then
  276.       package.loaded.term.setCursor(1, 1)
  277.     else
  278.       require("term").setCursor(1, 1)
  279.     end
  280.   end
  281.  
  282.   return this
  283. end
  284. local term = newTerm()
  285.  
  286. -- Self contained list object
  287. -- Requires: term object !!
  288. local function newList(x, y, width, height)
  289.   local this = {}
  290.  
  291.   this._x = 1
  292.   this._y = 1
  293.   this._w = 0
  294.   this._h = 0
  295.   this.width = 0
  296.   this.height = 0
  297.   this._nf = "white"
  298.   this._nb = "gray"
  299.   this._hf = "black"
  300.   this._hb = "orange"
  301.   this._le = {}
  302.   this._si = 1
  303.   this._pi = 0
  304.   this._lo = 0
  305.   this._ov = true
  306.   this._ur = true
  307.   this._urf = true
  308.  
  309.   function this:clear(all)
  310.     self._le = nil
  311.     self._le = {}
  312.     if (all) then
  313.       self._x = 1
  314.       self._y = 1
  315.       self._w = 0
  316.       self._h = 0
  317.       self.width = 0
  318.       self.height = 0
  319.       self._si = 1
  320.       self._lo = 0
  321.       self._ur = true
  322.       self._urf = true
  323.     end
  324.     os.sleep(0)
  325.   end
  326.   function this:setXY(x, y)
  327.     if (tonumber(x) and tonumber(y)) then
  328.       if (self._x ~= x or self._y ~= y) then
  329.         self._x = x
  330.         self._y = y
  331.         self._ur = true
  332.         self._urf = true
  333.       end
  334.     end
  335.   end
  336.   function this:getXY()
  337.     return self._x, self._y
  338.   end
  339.   function this:setWidth(width)
  340.     if (tonumber(width)) then
  341.       if (self._w ~= width) then
  342.         self._w, self.width = width, width
  343.         self._ur = true
  344.         self._urf = true
  345.       end
  346.     end
  347.   end
  348.   function this:setHeight(height)
  349.     if (tonumber(height)) then
  350.       if (self._h ~= height) then
  351.         self._h, self.height = height, height
  352.         self._ur = true
  353.         self._urf = true
  354.       end
  355.     end
  356.   end
  357.   function this:setSize(width, height)
  358.     if (tonumber(width) and tonumber(height)) then
  359.       if (self._w ~= width or self._h ~= height) then
  360.         self._w, self.width = width, width
  361.         self._h, self.height = height, height
  362.         self._ur = true
  363.         self._urf = true
  364.       end
  365.     end
  366.   end
  367.   function this:getSize()
  368.     return self._w, self._h
  369.   end
  370.   function this:setNormal(fore, back)
  371.     if (type(fore) == "string" and type(back) == "string") then
  372.       self._nf, self._nb = fore, back
  373.     end
  374.   end
  375.   function this:setHighlight(fore, back)
  376.     if (type(fore) == "string" and type(back) == "string") then
  377.       self._hf, self._hb = fore, back
  378.     end
  379.   end
  380.   function this:add(entry)
  381.     if (entry) then
  382.       table.insert(self._le, tostring(entry))
  383.       self._ur, self._urf = true, true
  384.     end
  385.   end
  386.   function this:setList(list)
  387.     if (type(list) == "table") then
  388.       self._le = nil
  389.       self._le = {}
  390.       for l = 1, #list do
  391.         table.insert(self._le, tostring(list[l]))
  392.       end
  393.       os.sleep(0)
  394.       self._ur, self._urf = true, true
  395.     end
  396.   end
  397.   function this:getList()
  398.     return self._le
  399.   end
  400.   function this:getSelectedIndex()
  401.     if (#self._le > 0) then
  402.       return self._si
  403.     else
  404.       return 0
  405.     end
  406.   end
  407.   function this:getSelectedEntry()
  408.     if (#self._le > 0) then
  409.       return self._le[self._si]
  410.     else
  411.       return ""
  412.     end
  413.   end
  414.   function this:refresh()
  415.     self._ur = true
  416.     self._urf = true
  417.     return true
  418.   end
  419.   function this:render()
  420.     if (self._ur) then
  421.       local spc = true
  422.       for y = 1, self._h do
  423.         local i = self._lo + y
  424.         if (i <= #self._le) then
  425.           if (i == self._si) then
  426.             term:color(self._hf, self._hb)
  427.             spc = true
  428.           else
  429.             if ((i == self._pi or self._urf) and spc) then
  430.               term:color(self._nf, self._nb)
  431.               spc = false
  432.             end
  433.           end
  434.           if ((i == self._pi or self._urf) or i == self._si) then
  435.             local le = " "..self._le[i]..string.rep(" ", self._w)
  436.             if (#le > self._w) then
  437.               le = string.sub(le, 1, self._w)
  438.             end
  439.             term:writeXY(self._x, self._y + (y - 1), le)
  440.             le = nil
  441.           end
  442.         elseif (self._urf) then
  443.           if (spc) then
  444.             term:color(self._nf, self._nb)
  445.           end
  446.           term:writeXY(self._x, self._y + (y - 1), string.rep(" ", self._w))
  447.         end
  448.       end
  449.       spc = nil
  450.       self._urf = false
  451.       self._ur = false
  452.       os.sleep(0)
  453.     end
  454.   end
  455.   function this:_mud(key)
  456.     if (key == 200) then
  457.       if (self._si > 1) then
  458.         self._pi = self._si
  459.         self._si = self._si - 1
  460.         if (self._si < self._lo + 1) then
  461.           self._lo = self._lo - 1
  462.           self._urf = true
  463.         end
  464.         self._ur = true
  465.       end
  466.     elseif (key == 208) then
  467.       if (self._si + 1 <= #self._le) then
  468.         self._pi = self._si
  469.         self._si = self._si + 1
  470.         if (self._si - self._lo > self._h) then
  471.           self._lo = self._lo + 1
  472.           self._urf = true
  473.         end
  474.         self._ur = true
  475.       end
  476.     end
  477.   end
  478.   function this:checkEvent(...)
  479.     local eventID, arg1, arg2, arg3, arg4, arg5 = table.unpack({...})
  480.     if (eventID == "key_down" and #self._le > 0) then
  481.       local key = tonumber(arg3)
  482.       if (key == 199) then -- Home
  483.         if (self._si > 1) then
  484.           self._pi = self._si
  485.           self._si = 1
  486.           if (self._lo > 0) then
  487.             self._lo = 0
  488.             self._urf = true
  489.           end
  490.           self._ur = true
  491.         end
  492.         return nil
  493.       elseif (key == 207) then -- End
  494.         if (self._si < #self._le) then
  495.           self._si = #self._le
  496.           self._pi = 0
  497.           self._lo = #self._le - self._h
  498.           if (self._lo < 0) then
  499.             self._lo = 0
  500.           end
  501.           self._ur = true
  502.           self._urf = true
  503.         end
  504.         return nil
  505.       elseif (key == 201) then -- PgUp
  506.         if (self._si > self._lo + 1) then
  507.           self._pi = self._si
  508.           self._si = self._lo + 1
  509.           self._ur = true
  510.         elseif (self._si == self._lo + 1) then
  511.           if (self._lo > 0) then
  512.             self._lo = self._lo - self._h
  513.             if (self._lo < 0) then
  514.               self._lo = 0
  515.             end
  516.             self._si = self._lo + 1
  517.             self._ur = true
  518.             self._urf = true
  519.           end
  520.         end
  521.         return nil
  522.       elseif (key == 209) then -- PgDn
  523.         if (self._si < self._lo + self._h) then
  524.           self._pi = self._si
  525.           self._si = self._lo + self._h
  526.           if (self._si > #self._le) then
  527.             self._si = #self._le
  528.           end
  529.           self._ur = true
  530.         else
  531.           if (self._si < #self._le) then
  532.             self._lo = self._lo + self._h
  533.             if (self._lo > #self._le - self._h) then
  534.               self._lo = #self._le - self._h
  535.             end
  536.             self._si = self._lo + self._h
  537.             if (self._si > #self._le) then
  538.               self._si = #self._le
  539.             end
  540.             self._pi = 0
  541.             self._ur = true
  542.             self._urf = true
  543.           end
  544.         end
  545.         return nil
  546.       elseif (key == 200) then -- Up
  547.         self:_mud(key)
  548.         return nil
  549.       elseif (key == 208) then -- Down
  550.         self:_mud(key)
  551.         return nil
  552.       elseif (key == 28) then -- Enter
  553.         return "listbox_select", arg1, self._si, self._le[self._si], arg4, nil
  554.       end
  555.     elseif (eventID == "touch" and #self._le > 0) then
  556.       local x, y = tonumber(arg2), tonumber(arg3)
  557.       if ((x >= self._x and x <= (self._x + self._w) - 1) and (y >= self._y and y <= (self._y + self._h) - 1)) then
  558.         if (arg4 == 0) then
  559.           local ni = self._lo + ((y - self._y) + 1)
  560.           if (ni == self._si) then
  561.             return "listbox_select", arg1, self._si, self._le[self._si], arg5, nil
  562.           elseif (ni <= #self._le) then
  563.             self._pi = self._si
  564.             self._si = ni
  565.             self._ur = true
  566.             return nil
  567.           end
  568.         end
  569.       end
  570.     elseif (eventID == "scroll" and #self._le > 0) then
  571.       local x, y = tonumber(arg2), tonumber(arg3)
  572.       if ((x >= self._x and x <= (self._x + self._w) - 1) and (y >= self._y and y <= (self._y + self._h) - 1)) then
  573.         if (arg4 == 1) then
  574.           self:_mud(200)
  575.           return nil
  576.         elseif (arg4 == -1) then
  577.           self:_mud(208)
  578.           return nil
  579.         end
  580.       end
  581.     end
  582.     return eventID, arg1, arg2, arg3, arg4, arg5
  583.   end
  584.   if (type(x) == "number" and type(y) == "number") then
  585.     this:setXY(x, y)
  586.   end
  587.   if (type(width) == "number" and type(height) == "number") then
  588.     this:setSize(width, height)
  589.   end
  590.  
  591.   return this
  592. end
  593.  
  594. -- ## StatusBar caption functions
  595. -- Splits an ampersand highlighted caption into a key/label pair
  596. local function fixCaption(caption)
  597.   local key, keyPos, label, work = 0, 0, "", ""
  598.   work = string.gsub(caption, "&&", ";amp;")
  599.   local amp = string.find(work, "&")
  600.   if (amp) then
  601.     key = string.sub(work, amp + 1, amp + 1)
  602.     keyPos = tonumber(amp)
  603.     label = string.gsub(string.gsub(work, "&", ""), ";amp;", "&")
  604.     return true, key, keyPos, label
  605.   end
  606.   return false, 0, 0, string.gsub(work, ";amp;", "&")
  607. end
  608.  
  609. -- Alternative key names for on-screen display
  610. local function getKeyName(keyNum)
  611.   local tbl = {
  612.     [59] = "F1",
  613.     [60] = "F2",
  614.     [61] = "F3",
  615.     [62] = "F4",
  616.     [63] = "F5",
  617.     [64] = "F6",
  618.     [65] = "F7",
  619.     [66] = "F8",
  620.     [67] = "F9",
  621.     [68] = "F10",
  622.     [87] = "F11",
  623.     [88] = "F12",
  624.     [183] = "PrnScr",
  625.     [70] = "ScrLck",
  626.     [197] = "Pause",
  627.     [41] = "Tilde",
  628.     [12] = "Minus",
  629.     [13] = "Equals",
  630.     [14] = "BckSpc",
  631.     [210] = "Ins",
  632.     [199] = "Home",
  633.     [201] = "PgUp",
  634.     [69] = "NumLck",
  635.     [181] = "Pad/",
  636.     [55] = "Pad*",
  637.     [74] = "Pad-",
  638.     [15] = "Tab",
  639.     [26] = "LBracket",
  640.     [27] = "RBracket",
  641.     [43] = "BSlash",
  642.     [211] = "Del",
  643.     [207] = "End",
  644.     [209] = "PgDn",
  645.     [71] = "Pad7",
  646.     [72] = "Pad8",
  647.     [73] = "Pad9",
  648.     [78] = "Pad+",
  649.     [58] = "CpsLck",
  650.     [39] = "SColon",
  651.     [40] = "Apos",
  652.     [28] = "Enter",
  653.     [75] = "Pad4",
  654.     [76] = "Pad5",
  655.     [77] = "Pad6",
  656.     [42] = "LShift",
  657.     [51] = "Comma",
  658.     [52] = "Period",
  659.     [53] = "Slash",
  660.     [54] = "RShift",
  661.     [200] = "Up",
  662.     [79] = "Pad1",
  663.     [80] = "Pad2",
  664.     [81] = "Pad3",
  665.     [29] = "LCtrl",
  666.     [219] = "LSuper",
  667.     [56] = "Alt",
  668.     [57] = "Space",
  669.     [184] = "AltGr",
  670.     [220] = "RSuper",
  671.     [221] = "WMenu",
  672.     [157] = "RShift",
  673.     [203] = "Left",
  674.     [208] = "Down",
  675.     [205] = "Right",
  676.     [82] = "Pad0",
  677.     [83] = "Pad."
  678.   }
  679.   local res = tbl[keyNum] or "???"
  680.   tbl = nil
  681.   os.sleep(0)
  682.   return res
  683. end
  684.  
  685. -- Snippet: statusBar
  686. local function newStatusBar(...)
  687.   local this = {}
  688.  
  689.   this._nt = ""
  690.   this._ht = {}
  691.  
  692.   function this:set(...)
  693.     local labels = {...}
  694.     self._nt = ""
  695.     self._ht = nil
  696.     self._ht = {}
  697.     local x, keyCap = 2, ""
  698.     for l = 1, #labels do
  699.       if (type(labels[l]) == "number") then
  700.         keyCap = getKeyName(labels[l])
  701.       elseif (type(labels[l]) == "string") then
  702.         local success, key, keyPos, cap = fixCaption(labels[l])
  703.         if (not success) then
  704.           key = 0
  705.           keyPos = 0
  706.         end
  707.         if (#keyCap > 0) then
  708.           if (term.depth == 1) then
  709.             self._nt = self._nt.."["..keyCap.."]: "..cap
  710.           else
  711.             self._nt = self._nt.."["..string.rep(" ", #keyCap).."]: "..cap
  712.             table.insert(self._ht, { (x + 1), keyCap } )
  713.           end
  714.         else
  715.           if (keyPos > 0) then
  716.             if (term.depth == 1) then
  717.               self._nt = self._nt.."["..string.upper(key).."]: "..cap
  718.             else
  719.               self._nt = self._nt..cap
  720.               table.insert(self._ht, { (x + (keyPos - 1)), key } )
  721.             end
  722.           else
  723.             self._nt = self._nt..cap
  724.           end
  725.         end
  726.       end
  727.       if (type(labels[l]) == "string" and l < #labels) then
  728.         self._nt = self._nt..", "
  729.       end
  730.       x = #self._nt + 2
  731.     end -- for l
  732.     os.sleep(0)
  733.   end
  734.   function this:render()
  735.     if (self._nt ~= "") then
  736.       term:clearln(term.height, table.unpack(theme.desktop))
  737.       term:writeXY(2, term.height, self._nt)
  738.       if (#self._ht > 0) then
  739.         term:color(table.unpack(theme.highlight))
  740.         for h = 1, #self._ht do
  741.           term:writeXY(self._ht[h][1], term.height, self._ht[h][2])
  742.         end
  743.       end
  744.     end
  745.   end
  746.   this:set(...)
  747.  
  748.   return this
  749. end
  750. -- ### End of statusBar caption functions
  751.  
  752. -- Text padding; both left and right
  753. local function pad(text, size, pre)
  754.   local result = tostring(text)
  755.   while #result < size do
  756.     if (pre) then
  757.       result = " "..result
  758.     else
  759.       result = result.." "
  760.     end
  761.   end
  762.   return result
  763. end
  764.  
  765. -- Splitter function
  766. local function split(str, pat)
  767.   local r = {}
  768.   for s in string.gmatch(str, "[^"..pat.."]+") do
  769.     table.insert(r, s)
  770.   end
  771.   return r
  772. end
  773. -- Wrap text inside specified width
  774. local function wrapText(text, width)
  775.   local txt = split(text, "\n")
  776.   local l = {}
  777.   for t = 1, #txt do
  778.     local cl = ""
  779.     local msg = ""..txt[t]..""
  780.     for w in msg:gmatch("%S+%s*") do
  781.       if (#cl + #w >= width) then
  782.         table.insert(l, cl)
  783.         cl = w
  784.       else
  785.         cl = cl..w
  786.       end
  787.     end -- for
  788.     if (cl ~= "") then
  789.       table.insert(l, cl)
  790.     end
  791.   end
  792.   return l
  793. end
  794.  
  795. -- Remove preceeding & trailing spaces
  796. local function trim(text)
  797.   local txt = tostring(text)
  798.   if (string.sub(txt, 1, 1) == " ") then
  799.     while (#txt > 0 and string.sub(txt, 1, 1) == " ") do
  800.       txt = string.sub(txt, 2, #txt)
  801.     end
  802.   end
  803.   if (string.sub(txt, #txt, #txt) == " ") then
  804.     while (#txt > 0 and string.sub(txt, #txt, #txt) == " ") do
  805.       txt = string.sub(txt, 1, #txt - 1)
  806.     end
  807.   end
  808.   return txt
  809. end
  810.  
  811. -- Some doc's return "int" instead of "number"
  812. local function fixVars(v)
  813.   if (string.find(v, ",")) then
  814.     local lst = split(v, ",")
  815.     for l = 1, #lst do
  816.       if (string.lower(lst[l]) == "int") then
  817.         lst[l] = "number"
  818.       end
  819.     end
  820.     return table.concat(lst, ",")
  821.   else
  822.     if (string.lower(v) == "int") then
  823.       return "number"
  824.     end
  825.   end
  826.   return v
  827. end
  828.  
  829. -- Splits function parameters into browsable table structures
  830. local function splitParameters(params)
  831.   if (params ~= "" and params ~= nil) then
  832.     local aopt = false
  833.     if (string.sub(params, 1, 1) == "[" and string.sub(params, #params, #params) == "]") then
  834.       params = string.sub(params, 2, #params)
  835.       params = string.sub(params, 1, #params - 1)
  836.       aopt = true
  837.     end
  838.     local result = {}
  839.     local args = split(params, ",")
  840.     local an, at, aat, tn, vn, vt = "", "", "", ""
  841.     local opt, itl, ivl, vo, sr = false, false, false, false, false
  842.     local tl, vl = {}, {}
  843.     for a = 1, #args do
  844.       sr = false;vo = false
  845.       if (string.find(args[a], "[:]")) then
  846.         local ap = split(args[a], ":")
  847.         an = ap[1];at = ap[2];aat = ap[3] or ""
  848.       else
  849.         an = args[a];at = "";aat = ""
  850.       end
  851.       an = string.gsub(an, " ", "");at = string.gsub(at, " ", "");aat = string.gsub(aat, " ", "")
  852.       if (string.sub(at, #at, #at) == "[") then
  853.         at = string.gsub(at, "[[]", "")
  854.         if (string.find(at, "[?]")) then
  855.           at = string.gsub(at, "[?]", "");vo = true;sr = true
  856.         else
  857.           sr = true
  858.         end
  859.         opt = true
  860.       elseif (string.sub(an, 1, 1) == "[") then
  861.         an = string.sub(an, 2, #an)
  862.         sr = true
  863.         opt = true
  864.       elseif (string.sub(at, #at, #at) == "]" and opt) then
  865.         opt = false;at = string.gsub(at, "[]]", "");vo = true;sr = true
  866.       elseif (string.sub(at, 1, 1) == "{") then
  867.         tn = an;tl = {};itl = true;at = string.gsub(at, "[{]", "")
  868.         if (string.find(aat, "[?]")) then
  869.           aat = string.gsub(aat, "[?]", "")
  870.           table.insert(tl, { at, aat, true } )
  871.         else
  872.           table.insert(tl, { at, aat, false } )
  873.         end
  874.       elseif (string.find(at, "[{]")) then
  875.         local tp = split(at, "{")
  876.         if (tp[1] == "table") then
  877.           tn = an;tl = {};itl = true
  878.           if (string.find(aat, "[?]")) then
  879.             aat = string.gsub(aat, "[?]", "")
  880.             table.insert(tl, { tp[2], aat, true } )
  881.           else
  882.             table.insert(tl, { tp[2], aat, false } )
  883.           end
  884.         else
  885.           vn = an;vt = tp[1];vl = {};ivl = true
  886.           table.insert(vl, tp[2])
  887.         end
  888.       elseif (string.find(an, "[}]")) then
  889.         local tp = split(an, "}")
  890.         if (itl) then
  891.           table.insert(tl, { tp[1], "", false } )
  892.           if (tp[2] == "?") then
  893.             vo = true
  894.           end
  895.           sr = true;an = tn;at = "table"
  896.         elseif (ivl) then
  897.           table.insert(vl, tp[1])
  898.           if (tp[2] == "?") then
  899.             vo = true
  900.           end
  901.           sr = true;an = vn;at = vt
  902.         end
  903.       elseif (itl) then
  904.         if (string.find(at, "[?]")) then
  905.           at = string.gsub(at, "[?]", "")
  906.           table.insert(tl, { an, at, true } )
  907.         else
  908.           table.insert(tl, { an, at, false } )
  909.         end
  910.       elseif (ivl) then
  911.         table.insert(vl, an)
  912.       else
  913.         if (string.find(at, "[?]")) then
  914.           at = string.gsub(at, "[?]", "");vo = true;sr = true
  915.         elseif (opt) then
  916.           vo = true;sr = true
  917.         else
  918.           sr = true
  919.         end
  920.       end
  921.       if (sr) then
  922.         if (aopt) then
  923.           vo = true
  924.         end
  925.         if (itl) then
  926.           table.insert(result, { an, fixVars(at), vo, tl } )
  927.           itl = false;tl = {};tn = ""
  928.         elseif (ivl) then
  929.           table.insert(result, { an, fixVars(at), vo, vl } )
  930.           ivl = false;vl = {};vn = "";vt = ""
  931.         else
  932.           table.insert(result, { an, fixVars(at), vo, false } )
  933.         end
  934.       end
  935.     end
  936.     return result
  937.   end
  938.   return {}
  939. end
  940.  
  941. -- Splits returned documentation into browsable table structures
  942. local function splitDoc(methodName, methodType, methodDoc)
  943.   if (methodDoc) then
  944.     local code, doc, mn, mt, rt, params = "", "", methodName, "", "", ""
  945.     local ma = {}
  946.     local rem1, rem2 = string.find(methodDoc, "[--]")
  947.     if (rem1 and rem2) then
  948.       code = string.sub(methodDoc, 1, rem1 - 2)
  949.       doc = string.sub(methodDoc, rem2 + 3, #methodDoc)
  950.     end
  951.     if (code ~= "") then
  952.       local ps1, ps2 = string.find(code, "[(]")
  953.       local pe1, pe2 = string.find(code, "[)]")
  954.       if (ps1 and pe1) then
  955.         mt = string.sub(code, 1, ps1 - 1)
  956.         params = string.sub(code, ps1 + 1, pe1 - 1)
  957.         local tmp = string.sub(code, pe1 + 1, #code)
  958.         local pc1, pc2 = string.find(tmp, "[:]")
  959.         if (pc1) then
  960.           rt = fixVars(trim(string.sub(tmp, pc1 + 1, #tmp)))
  961.         end
  962.       end
  963.     end
  964.     return { mn, mt, splitParameters(params), rt, doc }
  965.   else
  966.     if (methodName == "address" and methodType == "string") then
  967.       return { methodName, methodType, false, false, "The address of this component." }
  968.     elseif (methodName == "type" and methodType == "string") then
  969.       return { methodName, methodType, false, false, "Which type this component is." }
  970.     elseif (methodName == "slot" and methodType == "number") then
  971.       return { methodName, methodType, false, false, "The slot this component is installed in." }
  972.     else
  973.       return { methodName, methodType, false, false, "No further documentation available." }
  974.     end
  975.   end
  976.   return nil
  977. end
  978.  
  979. local function findAddress(partial, addressList)
  980.   if (string.find(partial, "[..]") or string.find(partial, " ")) then
  981.     if (string.find(partial, "[..]")) then
  982.       partial = string.sub(partial, 1, string.find(partial, "[..]") - 1)
  983.     end
  984.     if (string.find(partial, " ")) then
  985.       partial = string.sub(partial, 1, string.find(partial, " ") - 1)
  986.     end
  987.   end
  988.   for a = 1, #addressList do
  989.     if (string.sub(addressList[a], 1, #partial) == partial) then
  990.       return addressList[a]
  991.     end
  992.   end
  993.   return partial
  994. end
  995.  
  996. -- Get a table of filesystem mounts:
  997. -- address, label, mode, path
  998. local function getMounts(address)
  999.   local result = {}
  1000.   for proxy, path in fs.mounts() do
  1001.     local label = proxy.getLabel() or ""
  1002.     local mode = proxy.isReadOnly() and "ro" or "rw"
  1003.     if (address) then
  1004.       if (proxy.address == address) then
  1005.         table.insert(result, { proxy.address, label, mode, path } )
  1006.       end
  1007.     else
  1008.       table.insert(result, { proxy.address, label, mode, path } )
  1009.     end
  1010.   end
  1011.   return result
  1012. end
  1013.  
  1014. -- Get a list of all attached/visible components
  1015. -- sorted by type, sub-sorted by address
  1016. local function getComponents()
  1017.   local result = {}
  1018.   local cal, ctl = {}, {}
  1019.   for a, t in components.list() do
  1020.     local found = false
  1021.     for c = 1, #ctl do
  1022.       if (ctl[c] == t) then
  1023.         found = true
  1024.         break
  1025.       end
  1026.     end
  1027.     if (not found) then
  1028.       table.insert(ctl, t)
  1029.     end
  1030.     if (not cal[t]) then
  1031.       cal[t] = {}
  1032.     end
  1033.     table.insert(cal[t], a)
  1034.   end
  1035.   table.sort(ctl)
  1036.   for c = 1, #ctl do
  1037.     local al = {}
  1038.     for a = 1, #cal[ctl[c]] do
  1039.       table.insert(al, cal[ctl[c]][a])
  1040.     end
  1041.     table.sort(al)
  1042.     table.insert(result, { ctl[c], al } )
  1043.     al = nil
  1044.   end
  1045.   ctl = nil
  1046.   cal = nil
  1047.   return result
  1048. end
  1049.  
  1050. local function showHelp()
  1051.   local statusBar = newStatusBar("&Quit", 14, "Close help screen")
  1052.   term:cleartb()
  1053.   statusBar:render()
  1054.   term:color(table.unpack(theme.desktop))
  1055.   term:writeXY(2, 3, "The following keys can be used to navigate")
  1056.   term:writeXY(2, 4, "and select on most screens.")
  1057.   term:writeXY(2, 6, "[ArrowKeys]: Move selection.")
  1058.   term:writeXY(2, 7, "[Enter]    : Select highlighted entry.")
  1059.   term:writeXY(2, 8, "[BckSpc]   : Back to previous menu.")
  1060.   term:writeXY(2, 10, "[M]        : Display mount information.")
  1061.   term:writeXY(15, 12, "Only on address screen.")
  1062.   term:writeXY(2, 14, "[Q]        : Quit and return to OS.")
  1063.   term:color(table.unpack(theme.desktopError))
  1064.   term:writeXY(15, 11, "FileSystems only!")
  1065.   local running = true
  1066.   local res
  1067.   while running do
  1068.     local e, p1, p2, p3, p4, p5 = event.pull()
  1069.     if ((e == "key_down" and p3 == 14) or (e == "touch" and p4 == 1)) then
  1070.       running = nil
  1071.     elseif (e == "key_down" and p3 == 16) then
  1072.       res = "quit"
  1073.       running = nil
  1074.     end
  1075.   end
  1076.   statusBar = nil
  1077.   return res
  1078. end
  1079.  
  1080. -- Check all the standard events
  1081. local function stdEvents(e, p1, p2, p3, p4, p5, ct)
  1082.   if (e) then
  1083.     if (e == "key_down") then
  1084.       if (p3 == 14) then
  1085.         return "menu_back"
  1086.       else
  1087.         local c = string.lower(uni.char(p2))
  1088.         if (p3 == 50 and ct == "filesystem") then -- "m"
  1089.           return "menu_mount"
  1090.         elseif (p3 == 35) then -- "h"
  1091.           if (showHelp() == "quit") then
  1092.             return "quit"
  1093.           else
  1094.             return "ui_refresh"
  1095.           end
  1096.         elseif (p3 == 16) then -- "q"
  1097.           return "quit"
  1098.         end
  1099.       end
  1100.     elseif (e == "touch" and p4 == 1) then
  1101.       return "menu_back"
  1102.     elseif (string.sub(e, 1, 10) == "component_") then
  1103.       -- Force main menu to refresh component list
  1104.       refreshComponents = true
  1105.     end
  1106.   end
  1107.   return nil
  1108. end
  1109.  
  1110. local function varName(componentType)
  1111.   local ren = {
  1112.     chest = {
  1113.       "copper", "crystal", "diamond", "dirtchest9000", "gold", "iron", "obsidian", "silver", "blockvacuumchest",
  1114.       "extrautils_filing_cabinet",
  1115.     },
  1116.     machine = {
  1117.       "tile_blockbuffer_item", "tileentityfluidcrafter", "blockcrafter", "tileentityhardmedrive", "tilechest",
  1118.       "tiledrive", "tileentityfluidfiller", "tileentityvibrationchamberfluid", "alloy_smelter", "blocksagmill",
  1119.       "auto_painter", "blockvat", "blockwirelesschargertileentity", "farming_station", "blocktransceiver",
  1120.       "blockpoweredspawner", "blocksliceandsplice", "blocksoulbinder", "blocktelepadtileentity",
  1121.       "blockattractor", "blockenchanter", "blockexperienceobelisk", "blockinhibitorobelisk", "blockkillerjoe",
  1122.       "blockspawnguard", "blockweatherobelisk",
  1123.     },
  1124.     loader    = { "adv__item__loader", "fluid_loader", "item_loader", },
  1125.     unloader  = { "adv__item__unloader", "fluid_unloader", "item_unloader", },
  1126.     dispenser = { "cart_dispenser", "train_dispenser", },
  1127.     export    = { "me_exportbus", },
  1128.     import    = { "me_importbus", },
  1129.     interface = { "me_interface", },
  1130.     tank      = { "tileentitycertustank", "blockreservoirtileentity", },
  1131.     generator = { "blockcombustiongenerator", "blockzombiegenerator", "stirling_generator", "generatorfurnace", },
  1132.     solar     = { "blocksolarpaneltileentity", "extrautils_generatorsolar", },
  1133.     power     = { "tile_blockcapacitorbank_name", },
  1134.     monitor   = { "tile_blockpowermonitor", "blockinventorypanel", },
  1135.     barrel    = { "mcp_mobius_betterbarrel", },
  1136.     trashcan  = { "tileentitytrashcanenergy", "tileentitytrashcanfluids", },
  1137.   }
  1138.   local repl = {
  1139.     "openperipheral_", "tile_thermalexpansion_device_", "tile_thermalexpansion_dynamo_",
  1140.     "tile_thermalexpansion_machine_", "tile_thermalexpansion_", "thermalexpansion_",
  1141.   }
  1142.   local repl2 = {
  1143.     "_basic_name", "_name",
  1144.   }
  1145.   local res = string.lower(componentType)
  1146.   for r = 1, #repl do
  1147.     local tmp = string.sub(res, 1, #repl[r])
  1148.     if (string.sub(res, 1, #repl[r]) == repl[r]) then
  1149.       res = string.sub(res, #tmp + 1, #res)
  1150.       for i = 1, #repl2 do
  1151.         tmp = string.sub(res, (#res - #repl2[i]) + 1, #res)
  1152.         if (tmp == repl2[i]) then
  1153.           res = string.sub(res, 1, (#res - #repl2[i]))
  1154.           break
  1155.         end
  1156.       end
  1157.       return res
  1158.     end
  1159.   end
  1160.   -- Check for renaming
  1161.   for nr, lst in pairs(ren) do
  1162.     for l = 1, #lst do
  1163.       if (res == lst[l]) then
  1164.         return nr
  1165.       end
  1166.     end
  1167.   end
  1168.   return componentType
  1169. end
  1170.  
  1171. local function shiftMore(show)
  1172.   term:color(table.unpack(theme.desktop))
  1173.   if (show) then
  1174.     term:rightXY(term.width + 3, term.height, "[     ]: "..uni.char(0x25bc).." view more "..uni.char(0x25bc))
  1175.     term:color(table.unpack(theme.highlight))
  1176.     term:rightXY(term.width - 17, term.height, "Shift")
  1177.   else
  1178.     term:rightXY(term.width, term.height, string.rep(" ", 23))
  1179.   end
  1180. end
  1181.  
  1182. local function clua(ct)
  1183.   term:color(table.unpack(theme.lua[ct]))
  1184. end
  1185.  
  1186. -- Menu: Method Details
  1187. local function menuDoc(componentType, doc)
  1188.   local objName = varName(componentType)
  1189.   if (#objName > 10) then
  1190.     objName = "peripheral"
  1191.   end
  1192.   local mn, mt = " "..doc[1].." ", doc[2]
  1193.   local x, y, methodx, methody, docY, docD = 1, 1, 0, 0, 0, 0
  1194.   local hit = {}
  1195.   local docLines = wrapText(doc[5], term.width - 2)
  1196.   local running = true
  1197.   local refreshUI, uiFull, ms, sh, cb = true, true, true, false, false
  1198.   local pi, opi = 1, 0
  1199.   local res
  1200.  
  1201.   local statusBar = newStatusBar(14, "Back")
  1202.   while running do
  1203.     if (refreshUI) then
  1204.       if (uiFull) then
  1205.         hit = {}
  1206.         term:cleartb()
  1207.         statusBar:render()
  1208.        
  1209.         term:color(table.unpack(theme.desktopGray))
  1210.         term:writeXY(2, 3, componentType)
  1211.        
  1212.         if (doc[3]) then
  1213.           local rts = { "nil" }
  1214.           if (doc[4]) then
  1215.             if (string.find(doc[4], " or ")) then
  1216.               rts = split(string.gsub(doc[4], " or ", "----"), "----")
  1217.             else
  1218.               rts = { doc[4] }
  1219.             end
  1220.           end
  1221.           if (rts[1] == "") then
  1222.             rts[1] = "nil"
  1223.           end
  1224.           if (rts[1] == "nil") then
  1225.             clua("consts")
  1226.           else
  1227.             clua("text")
  1228.           end
  1229.           term:writeXY(2, 5, rts[1])
  1230.           clua("operators")
  1231.           term:write(" = ")
  1232.           clua("functs")
  1233.           term:writeXY(4, 6, "function")
  1234.           clua("text")
  1235.           methodx, methody = term:getXY()
  1236.           term:write(mn)
  1237.           clua("delims")
  1238.           if (#doc[3] > 0) then
  1239.             term:write("(")
  1240.             x, y = 5, 7
  1241.             term:gotoXY(x, y)
  1242.             for p = 1, #doc[3] do
  1243.               x, y = term:getXY()
  1244.               if (x + #doc[3][p][1] + 3 > term.width) then
  1245.                 x = 5
  1246.                 y = y + 1
  1247.                 term:gotoXY(x, y)
  1248.               end
  1249.               if (doc[3][p][3]) then
  1250.                 clua("optionals")
  1251.               else
  1252.                 clua("text")
  1253.               end
  1254.               local arg = " "..doc[3][p][1].." "
  1255.               table.insert(hit, { x, y, arg } )
  1256.               term:write(arg)
  1257.               if (p < #doc[3]) then
  1258.                 clua("delims")
  1259.                 term:write(",")
  1260.               end
  1261.             end
  1262.             y = y + 1
  1263.             clua("delims")
  1264.             term:writeXY(4, y, ")")
  1265.           else
  1266.             term:write("()")
  1267.           end
  1268.           x, y = term:getXY()
  1269.           docY = y + 2
  1270.         else
  1271.           clua("text")
  1272.           term:writeXY(2, 5, mt)
  1273.           clua("operators")
  1274.           term:write(" = ")
  1275.           clua("optionals")
  1276.           term:write(objName)
  1277.           clua("operators")
  1278.           term:write(".")
  1279.           clua("text")
  1280.           term:write(trim(mn))
  1281.           x, y = term:getXY()
  1282.           docY = y + 2
  1283.         end
  1284.         uiFull = false
  1285.       end -- uiFull
  1286.       docD = term.height - docY
  1287.       if (opi > 0) then
  1288.         if (doc[3][opi][3]) then
  1289.           clua("optionals")
  1290.         else
  1291.           clua("text")
  1292.         end
  1293.         term:writeXY(hit[opi][1], hit[opi][2], hit[opi][3])
  1294.         opi = 0
  1295.       end
  1296.       if (ms) then
  1297.         -- Method mode
  1298.         if (type(doc[3]) == "table") then
  1299.           if (#doc[3] > 0) then
  1300.             term:color(table.unpack(theme.menu.highlight))
  1301.             term:writeXY(methodx, methody, mn)
  1302.           end
  1303.         end
  1304.         term:color(table.unpack(theme.desktop))
  1305.         term:drawRect(1, docY, term.width, term.height - docY, " ")
  1306.         if (#docLines > docD) then
  1307.           if (keyb.isShiftDown()) then
  1308.             term:drawRect(1, 3, term.width, term.height - 3, " ")
  1309.             for d = 1, #docLines do
  1310.               if (d + 2 < term.height) then
  1311.                 term:writeXY(2, d + 2, docLines[d])
  1312.               end
  1313.             end
  1314.             shiftMore(false)
  1315.           else
  1316.             for d = 1, docD do
  1317.               term:writeXY(2, (docY + d) - 1, docLines[d])
  1318.             end
  1319.             shiftMore(true)
  1320.           end
  1321.         else
  1322.           for d = 1, #docLines do
  1323.             term:writeXY(2, (docY + d) - 1, docLines[d])
  1324.           end
  1325.         end
  1326.       elseif (doc[3] and pi > 0) then
  1327.         -- Parameter mode
  1328.         term:color(table.unpack(theme.menu.highlight))
  1329.         term:writeXY(hit[pi][1], hit[pi][2], hit[pi][3])
  1330.        
  1331.         term:color(table.unpack(theme.desktop))
  1332.         term:drawRect(1, docY, term.width, term.height - docY, " ")
  1333.         if (doc[3][pi][3]) then
  1334.           clua("optionals")
  1335.           term:writeXY(2, docY, "Parameter "..pi.." (optional):")
  1336.         else
  1337.           clua("text")
  1338.           term:writeXY(2, docY, "Parameter "..pi..":")
  1339.         end
  1340.         term:writeXY(2, docY + 2, doc[3][pi][1])
  1341.         clua("operators")
  1342.         term:write(" : ")
  1343.         if (doc[3][pi][3]) then
  1344.           clua("optionals")
  1345.         else
  1346.           clua("text")
  1347.         end
  1348.         term:write(doc[3][pi][2])
  1349.         if (doc[3][pi][4]) then
  1350.           if (doc[3][pi][2] == "table") then
  1351.             local mtn, mtt = 0, 0
  1352.             local startY = docY + 1
  1353.             for t = 1, #doc[3][pi][4] do
  1354.               if (#doc[3][pi][4][t][1] > mtn) then
  1355.                 mtn = #doc[3][pi][4][t][1]
  1356.               end
  1357.               if (#doc[3][pi][4][t][2] > mtt) then
  1358.                 mtt = #doc[3][pi][4][t][2]
  1359.               end
  1360.             end
  1361.             local col1 = 50 - (mtn + mtt + 4)
  1362.             local sep = col1 + mtn + 1
  1363.             local col2 = sep + 2
  1364.             -- Render the contents...
  1365.             clua("text")
  1366.             term:writeXY(col1, startY - 1, doc[3][pi][1])
  1367.             clua("operators")
  1368.             term:write(" = ")
  1369.             clua("delims")
  1370.             term:write("{")
  1371.             for t = 1, #doc[3][pi][4] do
  1372.               if (doc[3][pi][4][t][3]) then
  1373.                 clua("optionals")
  1374.                 term:writeXY(col1 - 1, (startY + t) - 1, "("..doc[3][pi][4][t][1])
  1375.               else
  1376.                 clua("text")
  1377.                 term:writeXY(col1, (startY + t) - 1, doc[3][pi][4][t][1])
  1378.               end
  1379.               if (doc[3][pi][4][t][2] ~= "") then
  1380.                 clua("operators")
  1381.                 term:writeXY(sep, (startY + t) - 1, ":")
  1382.                 if (doc[3][pi][4][t][3]) then
  1383.                   clua("optionals")
  1384.                   term:writeXY(col2, (startY + t) - 1, doc[3][pi][4][t][2]..")")
  1385.                 else
  1386.                   clua("text")
  1387.                   term:writeXY(col2, (startY + t) - 1, doc[3][pi][4][t][2])
  1388.                 end
  1389.               else
  1390.                 if (doc[3][pi][4][t][3]) then
  1391.                   term:write(")")
  1392.                 end
  1393.               end
  1394.             end
  1395.             clua("delims")
  1396.             term:rightXY(col1, startY + #doc[3][pi][4], "}")
  1397.           else
  1398.             term:color(table.unpack(theme.desktop))
  1399.             term:writeXY(2, docY + 4, "Values: ")
  1400.             for v = 1, #doc[3][pi][4] do
  1401.               local txt = doc[3][pi][4][v]
  1402.               local vx, vy = term:getXY()
  1403.               if (vx + #txt + 2 > term.width) then
  1404.                 term:gotoXY(10, vy + 1)
  1405.               end
  1406.               term:write(txt)
  1407.               if (v < #doc[3][pi][4]) then
  1408.                 term:write(", ")
  1409.               end
  1410.             end
  1411.           end
  1412.         end
  1413.       end
  1414.      
  1415.       refreshUI = false
  1416.     end
  1417.    
  1418.     local e, p1, p2, p3, p4, p5 = event.pull()
  1419.     local r = stdEvents(e, p1, p2, p3, p4, p5)
  1420.     if (r == "menu_back") then
  1421.       running = nil
  1422.     elseif (r == "ui_refresh") then
  1423.       uiFull = true
  1424.       refreshUI = true
  1425.     elseif (r == "quit") then
  1426.       res = "quit"
  1427.       running = nil
  1428.     elseif (e) then
  1429.       if (e == "key_down") then
  1430.         if (ms) then
  1431.           if (#docLines > docD) then
  1432.             if (keyb.isShiftDown() and not sh) then
  1433.               cb = cur.en
  1434.               cur.en = false
  1435.               sh = true
  1436.               refreshUI = true
  1437.             elseif (not keyb.isShiftDown() and sh) then
  1438.               sh = false
  1439.               uiFull = true
  1440.               refreshUI = true
  1441.               cur.en = cb
  1442.             end
  1443.           end
  1444.           if (doc[3] and p3 == 208) then -- arrow down: switch to parameter mode
  1445.             if (#doc[3] > 0) then
  1446.               pi = 1
  1447.               ms = false
  1448.               clua("text")
  1449.               term:writeXY(methodx, methody, mn)
  1450.               if (#docLines > docD) then
  1451.                 shiftMore(false)
  1452.               end
  1453.               refreshUI = true
  1454.             end
  1455.           end
  1456.         else -- if ms
  1457.           if (doc[3] and p3 == 200) then -- arrow up: switch to method mode
  1458.             opi = pi
  1459.             pi = 1
  1460.             ms = true
  1461.             if (#docLines > docD) then
  1462.               shiftMore(true)
  1463.             end
  1464.             refreshUI = true
  1465.           elseif (doc[3] and p3 == 203) then
  1466.             -- previous parameter
  1467.             if (pi > 1) then
  1468.               opi = pi
  1469.               pi = pi - 1
  1470.               refreshUI = true
  1471.             end
  1472.           elseif (doc[3] and p3 == 205) then
  1473.             -- next parameter
  1474.             if (pi < #doc[3]) then
  1475.               opi = pi
  1476.               pi = pi + 1
  1477.               refreshUI = true
  1478.             end
  1479.           end
  1480.         end -- if ms
  1481.       elseif (e == "touch") then
  1482.         if (p4 == 0) then -- left
  1483.           local tx, ty = p2, p3
  1484.           if ((tx >= methodx and tx <= methodx + (#mn - 1)) and (ty == methody)) then
  1485.             if (not ms) then
  1486.               opi = pi
  1487.               pi = 1
  1488.               ms = true
  1489.               if (#docLines > docD) then
  1490.                 shiftMore(true)
  1491.               end
  1492.               refreshUI = true
  1493.             end
  1494.           else
  1495.             -- Loop through any argument hits
  1496.             for h = 1, #hit do
  1497.               if ((tx >= hit[h][1] and tx <= hit[h][1] + (#hit[h][3] - 1)) and (ty == hit[h][2])) then
  1498.                 if (ms) then
  1499.                   pi = h
  1500.                   ms = false
  1501.                   clua("text")
  1502.                   term:writeXY(methodx, methody, mn)
  1503.                   if (#docLines > docD) then
  1504.                     shiftMore(false)
  1505.                   end
  1506.                   refreshUI = true
  1507.                   break
  1508.                 else
  1509.                   opi = pi
  1510.                   pi = h
  1511.                   refreshUI = true
  1512.                   break
  1513.                 end
  1514.               end
  1515.             end
  1516.           end
  1517.         end
  1518.       end
  1519.     end
  1520.   end
  1521.  
  1522.   statusBar = nil
  1523.   os.sleep(0)
  1524.   return res
  1525. end
  1526.  
  1527. -- Menu: Component Methods List
  1528. local function menuMethods(componentType, address)
  1529.   local running = true
  1530.   local refreshUI = true
  1531.   local docList = {}
  1532.   local statusBar = newStatusBar(14, "Back", 28, "View")
  1533.   local list = newList(1, 4, term.width, term.height - 5)
  1534.   local dev, reason = components.proxy(address)
  1535.   if (dev) then
  1536.     local lst = {}
  1537.     for n, v in pairs(dev) do
  1538.       table.insert(lst, n)
  1539.     end
  1540.     table.sort(lst)
  1541.     for l = 1, #lst do
  1542.       table.insert(docList, splitDoc(lst[l], type(dev[lst[l]]), components.doc(address, lst[l])))
  1543.     end
  1544.     lst = nil
  1545.     for d = 1, #docList do
  1546.       list:add(docList[d][1])
  1547.     end
  1548.   end
  1549.   local res
  1550.   while running do
  1551.     if (refreshUI) then
  1552.       term:cleartb()
  1553.       statusBar:render()
  1554.       if (dev) then
  1555.         term:color(table.unpack(theme.desktop))
  1556.         term:centerY(2, componentType)
  1557.       else
  1558.         term:color(table.unpack(theme.desktopError))
  1559.         term:centerY(3, "Component error!")
  1560.         term:color(table.unpack(theme.desktop))
  1561.         term:writeXY(2, 5, componentType)
  1562.         term:writeXY(2, 6, address)
  1563.         if (reason) then
  1564.           term:writeXY(2, 8, reason)
  1565.         else
  1566.           term:writeXY(2, 8, "Unable to connect to component")
  1567.         end
  1568.       end
  1569.       refreshUI = false
  1570.     end
  1571.     list:render()
  1572.     local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull())
  1573.     if (e == "listbox_select") then
  1574.       if (menuDoc(componentType, docList[p2]) == "quit") then
  1575.         res = "quit"
  1576.         running = nil
  1577.       else
  1578.         refreshUI = list:refresh()
  1579.       end
  1580.     elseif (e) then
  1581.       r = stdEvents(e, p1, p2, p3, p4, p5)
  1582.       if (r == "menu_back") then
  1583.         running = nil
  1584.       elseif (r == "ui_refresh") then
  1585.         refreshUI = list:refresh()
  1586.       elseif (r == "quit") then
  1587.         res = "quit"
  1588.         running = nil
  1589.       end
  1590.     end
  1591.   end
  1592.   dev = nil
  1593.   list:clear(true)
  1594.   list = nil
  1595.   statusBar = nil
  1596.   os.sleep(0)
  1597.   return res
  1598. end
  1599.  
  1600. -- Menu: FileSystem Mounts
  1601. local function menuMounts(address)
  1602.   local running = true
  1603.   local refreshUI = true
  1604.   local mounts = getMounts(address)
  1605.   local statusBar = newStatusBar("&Quit", 14, "Back")
  1606.   while running do
  1607.     if (refreshUI) then
  1608.       term:cleartb()
  1609.       statusBar:render()
  1610.       term:color(table.unpack(theme.desktop))
  1611.       term:writeXY(2, 3, "Filesystem on:")
  1612.       term:writeXY(2, 4, address)
  1613.       local y = 6
  1614.       for m = 1, #mounts do
  1615.         term:color(table.unpack(theme.desktop))
  1616.         term:writeXY(2, y + 0, "Label:")
  1617.         term:writeXY(2, y + 1, "Mode :")
  1618.         term:writeXY(2, y + 2, "Path :")
  1619.         if (mounts[m][2] == "") then
  1620.           term:color(table.unpack(theme.desktopWarning))
  1621.           term:writeXY(9, y + 0, "{No label set}")
  1622.         else
  1623.           term:writeXY(9, y + 0, mounts[m][2])
  1624.         end
  1625.         if (mounts[m][3] == "ro") then
  1626.           term:color(table.unpack(theme.desktopError))
  1627.           term:writeXY(9, y + 1, "[ro] Read Only")
  1628.         else
  1629.           term:color(table.unpack(theme.desktopAccept))
  1630.           term:writeXY(9, y + 1, "[rw] Read Write")
  1631.         end
  1632.         term:color(table.unpack(theme.desktop))
  1633.         term:writeXY(9, y + 2, mounts[m][4])
  1634.         y = y + 4
  1635.       end
  1636.       refreshUI = false
  1637.     end
  1638.     local r = stdEvents(event.pull())
  1639.     if (r == "menu_back") then
  1640.       running = nil
  1641.     elseif (r == "ui_refresh") then
  1642.       refreshUI = true
  1643.     elseif (r == "quit") then
  1644.       return "quit"
  1645.     end
  1646.   end
  1647.   statusBar = nil
  1648.   os.sleep(0)
  1649. end
  1650.  
  1651. -- Menu: Component address selection
  1652. local function menuAddress(componentType, addressList)
  1653.   local running = true
  1654.   local refreshUI = true
  1655.   local statusBar = nil
  1656.   if (componentType == "filesystem") then
  1657.     statusBar = newStatusBar("&Mount" , 14, "Back", 28, "View")
  1658.   else
  1659.     statusBar = newStatusBar(14, "Back", 28, "View")
  1660.   end
  1661.   local list = newList(1, 3, term.width, term.height - 4)
  1662.   if (componentType == "filesystem") then
  1663.     local mounts = getMounts()
  1664.     local address, label, mode, path
  1665.     local mwl, mwp = 0, 0
  1666.     for a = 1, #addressList do
  1667.       local count = 0
  1668.       for m = 1, #mounts do
  1669.         if (#mounts[m][2] > mwl) then
  1670.           mwl = #mounts[m][2]
  1671.         end
  1672.         if (#mounts[m][4] > mwp) then
  1673.           mwp = #mounts[m][4]
  1674.         end
  1675.         if (mounts[m][1] == addressList[a]) then
  1676.           address, label, mode, path = mounts[m][1], mounts[m][2], mounts[m][3], mounts[m][4]
  1677.           count = count + 1
  1678.         end
  1679.       end
  1680.       -- spacing + address + spacing + "[??] " + label + spacing + path + spacing
  1681.       if (term.width >= 1 + #addressList[1] + 1 + 5 + mwl + 1 + mwp + 1) then
  1682.         if (count > 1) then
  1683.           list:add(addressList[a].." "..pad(tostring(count), 4, true).." mounts")
  1684.         else
  1685.           list:add(addressList[a].." ["..mode.."] "..pad(label, mwl).." "..path)
  1686.         end
  1687.       else
  1688.         local diff = term.width - (mwl + mwp + 11)
  1689.         if (diff < 5) then
  1690.           diff = 5
  1691.         end
  1692.         if (count > 1) then
  1693.           list:add(string.sub(addressList[a], 1, diff)..".. "..pad(tostring(count), 4, true).." mounts")
  1694.         else
  1695.           list:add(string.sub(addressList[a], 1, diff)..".. ["..mode.."] "..pad(label, mwl).." "..path)
  1696.         end
  1697.       end
  1698.     end
  1699.   else
  1700.     local cap = componentType
  1701.     if (#cap > #addressList[1]) then
  1702.       local parts = split(cap, "_")
  1703.       local s = #parts
  1704.       cap = ""
  1705.       while #cap < #addressList[1] do
  1706.         cap = parts[1].."_..."
  1707.         for p = s, #parts do
  1708.           cap = cap.."_"..parts[p]
  1709.         end
  1710.         s = s - 1
  1711.       end
  1712.       s = s + 2
  1713.       cap = parts[1].."_..."
  1714.       for p = s, #parts do
  1715.         cap = cap.."_"..parts[p]
  1716.       end
  1717.     end
  1718.     for a = 1, #addressList do
  1719.       if (term.width > 1 + #addressList[1] + 1 + #cap + 1) then
  1720.         list:add(addressList[a].." "..cap)
  1721.       else
  1722.         local diff = term.width - (#cap + 5)
  1723.         if (diff < 5) then
  1724.           diff = 5
  1725.         end
  1726.         list:add(string.sub(addressList[a], 1, diff)..".. "..cap)
  1727.       end
  1728.     end
  1729.   end
  1730.   local res
  1731.   while running do
  1732.     if (refreshUI) then
  1733.       term:cleartb()
  1734.       statusBar:render()
  1735.       refreshUI = false
  1736.     end
  1737.     list:render()
  1738.     local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull())
  1739.     if (e == "listbox_select") then
  1740.       local addr = findAddress(p3, addressList)
  1741.       if (menuMethods(componentType, addr) == "quit") then
  1742.         res = "quit"
  1743.         running = nil
  1744.       else
  1745.         refreshUI = list:refresh()
  1746.       end
  1747.     elseif (e) then
  1748.       r = stdEvents(e, p1, p2, p3, p4, p5, componentType)
  1749.       if (r == "menu_back") then
  1750.         running = nil
  1751.       elseif (r == "menu_mount") then
  1752.         local addr = findAddress(list:getSelectedEntry(), addressList)
  1753.         if (menuMounts(addr, addressList) == "quit") then
  1754.           res = "quit"
  1755.           running = nil
  1756.         else
  1757.           refreshUI = list:refresh()
  1758.         end
  1759.       elseif (r == "ui_refresh") then
  1760.         refreshUI = list:refresh()
  1761.       elseif (r == "quit") then
  1762.         res = "quit"
  1763.         running = nil
  1764.       end
  1765.     end
  1766.   end -- while
  1767.   list:clear(true)
  1768.   list = nil
  1769.   statusBar = nil
  1770.   os.sleep(0)
  1771.   return res
  1772. end
  1773.  
  1774. -- Menu: Component type selection
  1775. local function menuType()
  1776.   local running = true
  1777.   local refreshUI = true
  1778.   local cl = {}
  1779.   local ci = 1
  1780.   local statusBar = newStatusBar("&Quit", "&Help", 28, "View")
  1781.   local list = newList(1, 3, term.width, term.height - 4)
  1782.   while running do
  1783.     if (refreshComponents) then
  1784.       cl = getComponents()
  1785.       list:clear()
  1786.       for c = 1, #cl do
  1787.         list:add(cl[c][1])
  1788.       end
  1789.       refreshComponents = false
  1790.       refreshUI = true
  1791.     end
  1792.     if (refreshUI) then
  1793.       term:cleartb()
  1794.       statusBar:render()
  1795.       refreshUI = false
  1796.     end
  1797.     list:render()
  1798.     local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull())
  1799.     if (e == "listbox_select") then
  1800.       for c = 1, #cl do
  1801.         if (cl[c][1] == p3) then
  1802.           if (menuAddress(cl[c][1], cl[c][2]) == "quit") then
  1803.             running = nil
  1804.           else
  1805.             refreshUI = list:refresh()
  1806.           end
  1807.           break
  1808.         end
  1809.       end
  1810.     elseif (e) then
  1811.       local r = stdEvents(e, p1, p2, p3, p4, p5)
  1812.       if (r == "ui_refresh") then
  1813.         refreshUI = list:refresh()
  1814.       elseif (r == "quit") then
  1815.         running = nil
  1816.       end
  1817.     end
  1818.   end
  1819.   list:clear(true)
  1820.   list = nil
  1821.   statusBar = nil
  1822.   os.sleep(0)
  1823. end
  1824.  
  1825. loadTheme()
  1826. if (term.width > 80 and term.height > 25) then
  1827.   term:init(80, 25)
  1828. end
  1829. menuType()
  1830.  
  1831. -- Clean up globals
  1832. term:close()
  1833. term = nil -- clear: newTerm() object, not the term API!!
  1834. theme = nil
  1835. -- Clear library vars
  1836. components = nil
  1837. event = nil
  1838. keyb = nil
  1839. uni = nil
  1840. fs = nil
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement