Guest User

pi

a guest
Feb 17th, 2014
931
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 46.43 KB | None | 0 0
  1. --Imported from class
  2. -- From http://lua-users.org/wiki/SimpleLuaClasses
  3.  
  4. -- class.lua
  5. -- Compatible with Lua 5.1 (not 5.0).
  6. local class = { }
  7. function class.class(base, init)
  8.   local c = {}    -- a new class instance
  9.   if not init and type(base) == 'function' then
  10.     init = base
  11.     base = nil
  12.   elseif type(base) == 'table' then
  13.     -- our new class is a shallow copy of the base class!
  14.     for i,v in pairs(base) do
  15.       c[i] = v
  16.     end
  17.     c._base = base
  18.   end
  19.   -- the class will be the metatable for all its objects,
  20.   -- and they will look up their methods in it.
  21.   c.__index = c
  22.  
  23.   -- expose a constructor which can be called by <classname>(<args>)
  24.   local mt = {}
  25.   mt.__call =
  26.     function(class_tbl, ...)
  27.       local obj = {}
  28.       setmetatable(obj,c)
  29.       --if init then
  30.       --  init(obj,...)
  31. if class_tbl.init then
  32.   class_tbl.init(obj, ...)
  33.       else
  34.         -- make sure that any stuff from the base class is initialized!
  35.         if base and base.init then
  36.           base.init(obj, ...)
  37.         end
  38.       end
  39.       return obj
  40.     end
  41.  
  42.   c.init = init
  43.   c.is_a =
  44.     function(self, klass)
  45.       local m = getmetatable(self)
  46.       while m do
  47.         if m == klass then return true end
  48.         m = m._base
  49.       end
  50.       return false
  51.     end
  52.   setmetatable(c, mt)
  53.   return c
  54. end
  55.  
  56.  
  57. --Imported from Logger
  58. local Logger = { }
  59.  
  60. local debugMon
  61. local logServerId
  62. local logFile
  63. local logger = screenLogger
  64. local filteredEvents = {}
  65.  
  66. local function nopLogger(text)
  67. end
  68.  
  69. local function monitorLogger(text)
  70.   debugMon.write(text)
  71.   debugMon.scroll(-1)
  72.   debugMon.setCursorPos(1, 1)
  73. end
  74.  
  75. local function screenLogger(text)
  76.   local x, y = term.getCursorPos()
  77.   if x ~= 1 then
  78.     local sx, sy = term.getSize()
  79.     term.setCursorPos(1, sy)
  80.     --term.scroll(1)
  81.   end
  82.   print(text)
  83. end
  84.  
  85. local function wirelessLogger(text)
  86.   if logServerId then
  87.     rednet.send(logServerId, {
  88.       type = 'log',
  89.       contents = text
  90.     })
  91.   end
  92. end
  93.  
  94. local function fileLogger(text)
  95.   local mode = 'w'
  96.   if fs.exists(logFile) then
  97.     mode = 'a'
  98.   end
  99.   local file = io.open(logFile, mode)
  100.   if file then
  101.     file:write(text)
  102.     file:write('\n')
  103.     file:close()
  104.   end
  105. end
  106.  
  107. local function setLogger(ilogger)
  108.   logger = ilogger
  109. end
  110.  
  111.  
  112. function Logger.disableLogging()
  113.   setLogger(nopLogger)
  114. end
  115.  
  116. function Logger.setMonitorLogging(logServer)
  117.   debugMon = Util.wrap('monitor')
  118.   debugMon.setTextScale(.5)
  119.   debugMon.clear()
  120.   debugMon.setCursorPos(1, 1)
  121.   setLogger(monitorLogger)
  122. end
  123.  
  124. function Logger.setScreenLogging()
  125.   setLogger(screenLogger)
  126. end
  127.  
  128. function Logger.setWirelessLogging(id)
  129.   if id then
  130.     logServerId = id
  131.   end
  132.   setLogger(wirelessLogger)
  133. end
  134.  
  135. function Logger.setFileLogging(fileName)
  136.   logFile = fileName
  137.   fs.delete(fileName)
  138.   setLogger(fileLogger)
  139. end
  140.  
  141. function Logger.log(value)
  142.   if type(value) == 'table' then
  143.     for k,v in pairs(value) do
  144.       logger(k .. '=' .. tostring(v))
  145.     end
  146.   else
  147.     logger(tostring(value))
  148.   end
  149. end
  150.  
  151. function Logger.logNestedTable(t, indent)
  152.   for _,v in ipairs(t) do
  153.     if type(v) == 'table' then
  154.       log('table')
  155.       logNestedTable(v) --, indent+1)
  156.     else
  157.       log(v)
  158.     end
  159.   end
  160. end
  161.  
  162. function Logger.filterEvent(event)
  163.   table.insert(filteredEvents, event)
  164. end
  165.  
  166. function Logger.logEvent(event, p1, p2, p3, p4, p5)
  167.   local function param(p)
  168.     if p then
  169.       return ', ' .. tostring(p)
  170.     end
  171.     return ''
  172.   end
  173.   for _,v in pairs(filteredEvents) do
  174.     if event == v then
  175.       return
  176.     end
  177.   end
  178.   if event == 'rednet_message' then
  179.     local msg = p2
  180.     logger(param(event) ..  param(p1) ..  param(msg.type) ..  param(msg.contents))
  181.   elseif event ~= 'modem_message' then
  182.     logger(param(event) ..  param(p1) ..  param(p2) ..  param(p3) ..  param(p4) ..  param(p5))
  183.   end
  184. end
  185.  
  186.  
  187. --Imported from Util
  188. local Util = { }
  189.  
  190. function Util.loadAPI(name)
  191.   local dir = shell.dir()
  192.   if fs.exists(dir .. '/' .. name) then
  193.     os.loadAPI(dir .. '/' .. name)
  194.   elseif shell.resolveProgram(name) then
  195.     os.loadAPI(shell.resolveProgram(name))
  196.   elseif fs.exists('/rom/apis/' .. name) then
  197.     os.loadAPI('/rom/apis/' .. name)
  198.   else
  199.     os.loadAPI(name)
  200.   end
  201. end
  202.  
  203. function Util.wrap(inP)
  204.  
  205.   local wrapped
  206.  
  207.   for k,side in pairs(redstone.getSides()) do
  208.     sideType = peripheral.getType(side)
  209.     if sideType then
  210.       --Logger.log(sideType .. " on " .. side)
  211.       if sideType == inP then
  212.         if sideType == "modem" then
  213.           rednet.open(side)
  214.           return
  215.         else
  216.           wrapped = peripheral.wrap(side)
  217.         end
  218.       end
  219.     end
  220.   end
  221.  
  222.   if not wrapped then
  223.     error(inP .. " is not connected")
  224.   end
  225.  
  226.   return wrapped
  227. end
  228.  
  229. function Util.getSide(device)
  230.   for k,side in pairs(rs.getSides()) do
  231.     if peripheral.getType(side) == device then
  232.       return side
  233.     end
  234.   end
  235.   error(device .. " is not connected")
  236. end
  237.  
  238. function Util.hasDevice(device)
  239.   for k,side in pairs(rs.getSides()) do
  240.     if peripheral.getType(side) == device then
  241.       return true
  242.     end
  243.   end
  244.   return false
  245. end
  246.  
  247. function Util.tryTimed(timeout, f, ...)
  248.   local c = os.clock()
  249.   while not f(...) do
  250.     if os.clock()-c >= timeout then
  251.       return false
  252.     end
  253.   end
  254.   return true
  255. end
  256.  
  257. function Util.trace(x)
  258.   if not x or not x.is_a then
  259.     error('Incorrect syntax', 3)
  260.   end
  261. end
  262.  
  263. function Util.printTable(t)
  264.   if not t then
  265.     error('printTable: nil passed', 2)
  266.   end
  267.   for k,v in pairs(t) do
  268.     print(k .. '=' .. tostring(v))
  269.   end
  270. end
  271.  
  272. function Util.tableSize(t)
  273.   local c = 0
  274.   for _,_ in pairs(t)
  275.     do c = c+1
  276.   end
  277.   return c
  278. end
  279.  
  280. --https://github.com/jtarchie/underscore-lua
  281. function Util.each(list, func)
  282.   local pairing = pairs
  283.   if Util.isArray(list) then pairing = ipairs end
  284.  
  285.   for index, value in pairing(list) do
  286.     func(value, index, list)
  287.   end
  288. end
  289.  
  290. function Util.size(list, ...)
  291.   local args = {...}
  292.  
  293.   if Util.isArray(list) then
  294.     return #list
  295.   elseif Util.isObject(list) then
  296.     local length = 0
  297.     Util.each(list, function() length = length + 1 end)
  298.     return length
  299.   end
  300.  
  301.   return 0
  302. end
  303.  
  304. function Util.isObject(value)
  305.   return type(value) == "table"
  306. end
  307.  
  308. function Util.isArray(value)
  309.   return type(value) == "table" and (value[1] or next(value) == nil)
  310. end
  311. -- end https://github.com/jtarchie/underscore-lua
  312.  
  313. function Util.readFile(fname)
  314.   local f = fs.open(fname, "r")
  315.   if f then
  316.     local t = f.readAll()
  317.     f.close()
  318.     return t
  319.   end
  320. end
  321.  
  322. function Util.readTable(fname)
  323.   local t = Util.readFile(fname)
  324.   if t then
  325.     return textutils.unserialize(t)
  326.   end
  327.   Logger.log('Util:readTable: ' .. fname .. ' does not exist')
  328.   return { }
  329. end
  330.  
  331. function Util.writeTable(fname, data)
  332.   writeFile(fname, textutils.serialize(data))
  333. end
  334.  
  335. function Util.writeFile(fname, data)
  336.   local file = io.open(fname, "w")
  337.   if file then
  338.     file:write(data)
  339.     file:close()
  340.   end
  341. end
  342.  
  343. function Util.shallowCopy(t)
  344.   local t2 = {}
  345.   for k,v in pairs(t) do
  346.     t2[k] = v
  347.   end
  348.   return t2
  349. end
  350.  
  351. function Util.split(str)
  352.   local t = {}
  353.   local function helper(line) table.insert(t, line) return "" end
  354.   helper((str:gsub("(.-)\n", helper)))
  355.   return t
  356. end
  357.  
  358. string.lpad = function(str, len, char)
  359.     if char == nil then char = ' ' end
  360.     return str .. string.rep(char, len - #str)
  361. end
  362.  
  363. -- http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
  364. function Util.spairs(t, order)
  365.   if not t then
  366.     error('spairs: nil passed')
  367.   end
  368.  
  369.   -- collect the keys
  370.   local keys = {}
  371.   for k in pairs(t) do keys[#keys+1] = k end
  372.  
  373.   -- if order function given, sort by it by passing the table and keys a, b,
  374.   -- otherwise just sort the keys
  375.   if order then
  376.     table.sort(keys, function(a,b) return order(t[a], t[b]) end)
  377.   else
  378.     table.sort(keys)
  379.   end
  380.  
  381.   -- return the iterator function
  382.   local i = 0
  383.   return function()
  384.     i = i + 1
  385.     if keys[i] then
  386.       return keys[i], t[keys[i]]
  387.     end
  388.   end
  389. end
  390.  
  391. function Util.first(t, order)
  392.   -- collect the keys
  393.   local keys = {}
  394.   for k in pairs(t) do keys[#keys+1] = k end
  395.  
  396.   -- if order function given, sort by it by passing the table and keys a, b,
  397.   -- otherwise just sort the keys
  398.   if order then
  399.     table.sort(keys, function(a,b) return order(t[a], t[b]) end)
  400.   else
  401.     table.sort(keys)
  402.   end
  403.   return keys[1], t[keys[1]]
  404. end
  405.  
  406. --[[
  407. pbInfo - Libs/lib.WordWrap.lua
  408.     v0.41
  409.     by p.b. a.k.a. novayuna
  410.     released under the Creative Commons License By-Nc-Sa: http://creativecommons.org/licenses/by-nc-sa/3.0/
  411.    
  412.     original code by Tomi H.: http://shadow.vs-hs.org/library/index.php?page=2&id=48
  413. ]]
  414. function Util.WordWrap(strText, intMaxLength)
  415.     local tblOutput = {};
  416.     local intIndex;
  417.     local strBuffer = "";
  418.     local tblLines = Util.Explode(strText, "\n");
  419.     for k, strLine in pairs(tblLines) do
  420.         local tblWords = Util.Explode(strLine, " ");
  421.         if (#tblWords > 0) then
  422.             intIndex = 1;
  423.             while tblWords[intIndex] do
  424.                 local strWord = " " .. tblWords[intIndex];
  425.                 if (strBuffer:len() >= intMaxLength) then
  426.                     table.insert(tblOutput, strBuffer:sub(1, intMaxLength));
  427.                     strBuffer = strBuffer:sub(intMaxLength + 1);
  428.                 else
  429.                     if (strWord:len() > intMaxLength) then
  430.                         strBuffer = strBuffer .. strWord;
  431.                     elseif (strBuffer:len() + strWord:len() >= intMaxLength) then
  432.                         table.insert(tblOutput, strBuffer);
  433.                         strBuffer = ""
  434.                     else
  435.                         if (strBuffer == "") then
  436.                             strBuffer = strWord:sub(2);
  437.                         else
  438.                             strBuffer = strBuffer .. strWord;
  439.                         end;
  440.                         intIndex = intIndex + 1;
  441.                     end;
  442.                 end;
  443.             end;
  444.             if strBuffer ~= "" then
  445.                 table.insert(tblOutput, strBuffer);
  446.                 strBuffer = ""
  447.             end;
  448.         end;
  449.     end;
  450.     return tblOutput;
  451. end
  452.  
  453. function Util.Explode(strText, strDelimiter)
  454.     local strTemp = "";
  455.     local tblOutput = {};
  456. if not strText then
  457.   error('no strText', 4)
  458. end
  459.     for intIndex = 1, strText:len(), 1 do
  460.         if (strText:sub(intIndex, intIndex + strDelimiter:len() - 1) == strDelimiter) then
  461.             table.insert(tblOutput, strTemp);
  462.             strTemp = "";
  463.         else
  464.             strTemp = strTemp .. strText:sub(intIndex, intIndex);
  465.         end;
  466.     end;
  467.     if (strTemp ~= "") then
  468.         table.insert(tblOutput, strTemp)
  469.     end;
  470.     return tblOutput;
  471. end
  472.  
  473. -- http://lua-users.org/wiki/AlternativeGetOpt
  474. local function getopt( arg, options )
  475.   local tab = {}
  476.   for k, v in ipairs(arg) do
  477.     if string.sub( v, 1, 2) == "--" then
  478.       local x = string.find( v, "=", 1, true )
  479.       if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 )
  480.       else      tab[ string.sub( v, 3 ) ] = true
  481.       end
  482.     elseif string.sub( v, 1, 1 ) == "-" then
  483.       local y = 2
  484.       local l = string.len(v)
  485.       local jopt
  486.       while ( y <= l ) do
  487.         jopt = string.sub( v, y, y )
  488.         if string.find( options, jopt, 1, true ) then
  489.           if y < l then
  490.             tab[ jopt ] = string.sub( v, y+1 )
  491.             y = l
  492.           else
  493.             tab[ jopt ] = arg[ k + 1 ]
  494.           end
  495.         else
  496.           tab[ jopt ] = true
  497.         end
  498.         y = y + 1
  499.       end
  500.     end
  501.   end
  502.   return tab
  503. end
  504. -- end http://lua-users.org/wiki/AlternativeGetOpt
  505.  
  506. function Util.showOptions(options)
  507.   for k, v in pairs(options) do
  508.     print(string.format('-%s  %s', v.arg, v.desc))
  509.   end
  510. end
  511.  
  512. function Util.getOptions(options, args, syntaxMessage)
  513.   local argLetters = ''
  514.   for _,o in pairs(options) do
  515.     argLetters = argLetters .. o.arg
  516.   end
  517.   local rawOptions = getopt(args, argLetters)
  518.  
  519.   for _,o in pairs(options) do
  520.     if rawOptions[o.arg] then
  521.       o.value = rawOptions[o.arg]
  522.       if o.value and tonumber(o.value) then
  523.         o.value = tonumber(o.value)
  524.       end
  525.     end
  526.   end
  527.  
  528. --[[
  529. for k,v in pairs(options) do
  530.   print(k)
  531.   Util.printTable(v)
  532. end
  533. read()
  534. --]]
  535.  
  536. end
  537.  
  538.  
  539. --Imported from Event
  540. local Event = { }
  541.  
  542. local eventHandlers = {
  543.   namedTimers = {}
  544. }
  545. local enableQueue = {}
  546. local removeQueue = {}
  547.  
  548. local function deleteHandler(h)
  549.   for k,v in pairs(eventHandlers[h.event].handlers) do
  550.     if v == h then
  551.       table.remove(eventHandlers[h.event].handlers, k)
  552.       break
  553.     end
  554.   end
  555.   --table.remove(eventHandlers[h.event].handlers, h.key)
  556. end
  557.  
  558. function Event.addHandler(type, f)
  559.   local event = eventHandlers[type]
  560.   if not event then
  561.     event = {}
  562.     event.handlers = {}
  563.     eventHandlers[type] = event
  564.   end
  565.  
  566.   local handler = {}
  567.   handler.event = type
  568.   handler.f = f
  569.   handler.enabled = true
  570.   table.insert(event.handlers, handler)
  571.   -- any way to retrieve key here for removeHandler ?
  572.  
  573.   return handler
  574. end
  575.  
  576. function Event.removeHandler(h)
  577.   h.deleted = true
  578.   h.enabled = false
  579.   table.insert(removeQueue, h)
  580. end
  581.  
  582. function Event.queueTimedEvent(name, timeout, event, args)
  583.   Event.addNamedTimer(name, timeout, false,
  584.     function()
  585.       os.queueEvent(event, args)
  586.     end
  587.   )
  588. end
  589.  
  590. function Event.addNamedTimer(name, interval, recurring, f)
  591.   Event.cancelNamedTimer(name)
  592.   eventHandlers.namedTimers[name] = Event.addTimer(interval, recurring, f)
  593. end
  594.  
  595. function Event.getNamedTimer(name)
  596.   return eventHandlers.namedTimers[name]
  597. end
  598.  
  599. function Event.cancelNamedTimer(name)
  600.   local timer = Event.getNamedTimer(name)
  601.  
  602.   if timer then
  603.     timer.enabled = false
  604.     timer.recurring = false
  605.   end
  606. end
  607.  
  608. function Event.addTimer(interval, recurring, f)
  609.   local timer = Event.addHandler('timer',
  610.     function(t, id)
  611.       if t.timerId ~= id then
  612.         return
  613.       end
  614.       if t.enabled then
  615.         t.cf(t, id)
  616.       end
  617.       if t.recurring then
  618.         t.timerId = os.startTimer(t.interval)
  619.       else
  620.         removeHandler(t)
  621.       end
  622.     end
  623.   )
  624.   timer.cf = f
  625.   timer.interval = interval
  626.   timer.recurring = recurring
  627.   timer.timerId = os.startTimer(interval)
  628.  
  629.   return timer
  630. end
  631.  
  632. function Event.removeTimer(h)
  633.   Event.removeEventHandler(h)
  634. end
  635.  
  636. function Event.waitForEvent(event, timeout)
  637.   local c = os.clock()
  638.   while true do
  639.     os.queueEvent('dummyEvent')
  640.     local e, p1, p2, p3, p4 = Event.pullEvent()
  641.     if e == event then
  642.       return e, p1, p2, p3, p4
  643.     end
  644.     if os.clock()-c > timeout then
  645.       return
  646.     end
  647.   end
  648. end
  649.  
  650. function Event.pullEvents()
  651.   while true do
  652.     local e = Event.pullEvent()
  653.     if e == 'exitPullEvents' then
  654.       break
  655.     end
  656.   end
  657. end
  658.  
  659. function Event.exitPullEvents()
  660.   os.queueEvent('exitPullEvents')
  661. end
  662.  
  663. function Event.enableHandler(h)
  664.   table.insert(enableQueue, h)
  665. end
  666.  
  667. function Event.pullEvent(type)
  668.   local e, p1, p2, p3, p4, p5 = os.pullEvent(type)
  669.  
  670.   Logger.logEvent(e, p1, p2, p3, p4, p5)
  671.  
  672.   local event = eventHandlers[e]
  673.   if event then
  674.     for k,v in pairs(event.handlers) do
  675.       if v.enabled then
  676.         v.f(v, p1, p2, p3, p4, p5)
  677.       end
  678.     end
  679.     while #enableQueue > 0 do
  680.       table.remove(handlerQueue).enabled = true
  681.     end
  682.     while #removeQueue > 0 do
  683.       Event.deleteHandler(table.remove(removeQueue))
  684.     end
  685.   end
  686.  
  687.   return e, p1, p2, p3, p4, p5
  688. end
  689.  
  690. --Imported from UI
  691. local function widthify(s, len)
  692.   if not s then
  693.     s = ' '
  694.   end
  695.   return string.lpad(string.sub(s, 1, len) , len, ' ')
  696. end
  697.  
  698. local console
  699.  
  700. UI = { }
  701.  
  702. function UI.setProperties(obj, argTable)
  703.   if argTable then
  704.     for k,v in pairs(argTable) do
  705.       obj[k] = v
  706.     end
  707.   end
  708. end
  709.  
  710. -- there can be only one
  711. function UI.getConsole()
  712.   if not console then
  713.     console = UI.Console()
  714.   end
  715.   return console
  716. end
  717.  
  718. --[[-- Console (wrapped term) --]]--
  719. UI.Console = class.class()
  720.  
  721. function UI.Console:init(args)
  722.   self.clear = term.clear
  723.   self.width, self.height = term.getSize()
  724.   UI.setProperties(self, args)
  725.   self.isColor = term.isColor()
  726.   if not self.isColor then
  727.     term.setBackgroundColor = function(...) end
  728.     term.setTextColor = function(...) end
  729.   end
  730. end
  731.  
  732. function UI.Console:reset()
  733.   term.setCursorPos(1, 1)
  734.   term.setBackgroundColor(colors.black)
  735.   self:clear()
  736. end
  737.  
  738. function UI.Console:advanceCursorY(offset)
  739.   local x, y = term.getCursorPos()
  740.   term.setCursorPos(1, y+offset)
  741. end
  742.  
  743. function UI.Console:clearArea(x, y, width, height, bg)
  744.   if bg then
  745.     term.setBackgroundColor(bg)
  746.   end
  747.   local filler = string.rep(' ', width+1)
  748.   for i = y, y+height-1 do
  749.     term.setCursorPos(x, i)
  750.     term.write(filler)
  751.   end
  752. end
  753.  
  754. function UI.Console:write(x, y, text, bg)
  755.   if bg then
  756.     term.setBackgroundColor(bg)
  757.   end
  758.   term.setCursorPos(x, y)
  759.   term.write(tostring(text))
  760. end
  761.  
  762. function UI.Console:wrappedWrite(x, y, text, len, bg)
  763.   for k,v in pairs(Util.WordWrap(text, len)) do
  764.     console:write(x, y, v, bg)
  765.     y = y + 1
  766.   end
  767.   return y
  768. end
  769.  
  770. function UI.Console:prompt(text)
  771.   term.write(text)
  772.   term.setCursorBlink(true)
  773.   local response = read()
  774.   term.setCursorBlink(false)
  775.   if string.len(response) > 0 then
  776.     return response
  777.   end
  778. end
  779.  
  780. --[[-- StringBuffer --]]--
  781. UI.StringBuffer = class.class()
  782. function UI.StringBuffer:init(bufSize)
  783.   self.bufSize = bufSize
  784.   self.buffer = {}
  785. end
  786.  
  787. function UI.StringBuffer:insert(s, index)
  788.   table.insert(self.buffer, { index = index, str = s })
  789. end
  790.  
  791. function UI.StringBuffer:append(s)
  792.   local str = self:get()
  793.   self:insert(s, #str)
  794. end
  795.  
  796. function UI.StringBuffer:get()
  797.   local str = ''
  798.   for k,v in Util.spairs(self.buffer, function(a, b) return a.index < b.index end) do
  799.     str = str .. string.rep(' ', v.index - string.len(str)) .. v.str
  800.   end
  801.   local len = string.len(str)
  802.   if len < self.bufSize then
  803.     str = str .. string.rep(' ', self.bufSize - len)
  804.   end
  805.   return str
  806. end
  807.  
  808. function UI.StringBuffer:clear()
  809.   self.buffer = {}
  810. end
  811.  
  812. --[[-- Pager --]]--
  813. UI.Pager = class.class()
  814.  
  815. function UI.Pager:init(args)
  816.   local defaults = {
  817.     pages = { }
  818.   }
  819.   UI.setProperties(self, defaults)
  820.   UI.setProperties(self, args)
  821.  
  822.   self.keyHandler = Event.addHandler('mouse_scroll',
  823.     function(h, direction)
  824.       if direction == 1 then
  825.         self.currentPage:keyHandler('down')
  826.       else
  827.         self.currentPage:keyHandler('up')
  828.       end
  829.     end
  830.   )
  831.   self.keyHandler = Event.addHandler('char',
  832.     function(h, ch)
  833.       if self.currentPage then
  834.         self.currentPage:keyHandler(ch)
  835.       end
  836.     end
  837.   )
  838.   self.keyHandler = Event.addHandler('key',
  839.     function(h, code)
  840.       local ch = keys.getName(code)
  841.       -- filter out a through z as they will be get picked up
  842.       -- as char events
  843.       if string.len(ch) > 1 then
  844.         if self.currentPage then
  845.           self.currentPage:keyHandler(ch)
  846.         end
  847.       end
  848.     end
  849.   )
  850. end
  851.  
  852. function UI.Pager:addPage(name, page)
  853.   self.pages[name] = page
  854. end
  855.  
  856. function UI.Pager:setPages(pages)
  857.   self.pages = pages
  858. end
  859.  
  860. function UI.Pager:getPage(pageName, ...)
  861.   local page = self.pages[pageName]
  862.  
  863.   if not page then
  864.     error('Pager:getPage: Invalid page: ' .. tostring(pageName), 2)
  865.   end
  866.  
  867.   return page
  868. end
  869.  
  870. -- changing to setNamedPage
  871. function UI.Pager:setPage(pageName)
  872.   local page = self:getPage(pageName)
  873.  
  874.   self:setPageRaw(page)
  875. end
  876.  
  877. -- changing to setPage
  878. function UI.Pager:setPageRaw(page)
  879.   if page == self.currentPage then
  880.     page:draw()
  881.   else
  882.     if self.currentPage then
  883.       self.currentPage:disable()
  884.       self.currentPage.enabled = false
  885.       page.previousPage = self.currentPage
  886.     end
  887.     self.currentPage = page
  888.     console:reset()
  889.     page.enabled = true
  890.     page:enable()
  891.     page:draw()
  892.   end
  893. end
  894.  
  895. function UI.Pager:getCurrentPage()
  896.   return self.currentPage
  897. end
  898.  
  899. function UI.Pager:setDefaultPage()
  900.   if not self.defaultPage then
  901.     error('No default page defined', 2)
  902.   end
  903.   self:setPage(self.defaultPage)
  904. end
  905.  
  906. function UI.Pager:setPreviousPage()
  907.   if self.currentPage.previousPage then
  908.     local previousPage = self.currentPage.previousPage.previousPage
  909.     self:setPageRaw(self.currentPage.previousPage)
  910.     self.currentPage.previousPage = previousPage
  911.   end
  912. end
  913.  
  914. --[[-- Page --]]--
  915. UI.Page = class.class()
  916.  
  917. function UI.Page:init(args)
  918.   self.defaultPage = 'menu'
  919.   UI.setProperties(self, args)
  920. end
  921.  
  922. function UI.Page:draw()
  923. end
  924.  
  925. function UI.Page:enable()
  926. end
  927.  
  928. function UI.Page:disable()
  929. end
  930.  
  931. function UI.Page:keyHandler(ch)
  932. end
  933.  
  934. --[[-- Grid  --]]--
  935. UI.Grid = class.class()
  936.  
  937. function UI.Grid:init(args)
  938.   local defaults = {
  939.     sep = ' ',
  940.     sepLen = 1,
  941.     x = 1,
  942.     y = 1,
  943.     pageSize = 16,
  944.     pageNo = 1,
  945.     index = 1,
  946.     inverseSort = false,
  947.     disableHeader = false,
  948.     selectable = true,
  949.     textColor = colors.white,
  950.     textSelectedColor = colors.white,
  951.     backgroundColor = colors.black,
  952.     backgroundSelectedColor = colors.gray,
  953.     t = {},
  954.     columns = {}
  955.   }
  956.   UI.setProperties(self, defaults)
  957.   UI.setProperties(self, args)
  958.   if not self.width then
  959.     self.width = self:calculateWidth()
  960.   end
  961.   if self.autospace then
  962.     local colswidth = 0
  963.     for _,c in pairs(self.columns) do
  964.       colswidth = colswidth + c[3] + 1
  965.     end
  966.     local spacing = (self.width - colswidth - 1)
  967.     spacing = math.floor(spacing / (#self.columns - 1) )
  968.     for _,c in pairs(self.columns) do
  969.       c[3] = c[3] + spacing
  970.     end
  971.   end
  972. end
  973.  
  974. function UI.Grid:setPosition(x, y)
  975.   self.x = x
  976.   self.y = y
  977. end
  978.  
  979. function UI.Grid:setPageSize(pageSize)
  980.   self.pageSize = pageSize
  981. end
  982.  
  983. function UI.Grid:setColumns(columns)
  984.   self.columns = columns
  985. end
  986.  
  987. function UI.Grid:getTable()
  988.   return self.t
  989. end
  990.  
  991. function UI.Grid:setTable(t)
  992.   self.t = t
  993. end
  994.  
  995. function UI.Grid:setInverseSort(inverseSort)
  996.   self.inverseSort = inverseSort
  997.   self:drawRows()
  998. end
  999.  
  1000. function UI.Grid:setSortColumn(column)
  1001.   self.sortColumn = column
  1002.   for _,col in pairs(self.columns) do
  1003.     if col[2] == column then
  1004.       return
  1005.     end
  1006.   end
  1007.   error('Grid:setSortColumn: invalid column', 2)
  1008. end
  1009.  
  1010. function UI.Grid:setSeparator(sep)
  1011.   self.sep = sep
  1012.   self.sepLen = string.len(sep)
  1013. end
  1014.  
  1015. function UI.Grid:setSelected(row)
  1016.   self.selected = row
  1017. end
  1018.  
  1019. function UI.Grid:getSelected()
  1020.   return self.selected
  1021. end
  1022.  
  1023. function UI.Grid:draw()
  1024.   if not self.disableHeader then
  1025.     self:drawHeadings()
  1026.   end
  1027.   self:drawRows()
  1028. end
  1029.  
  1030. function UI.Grid:drawHeadings()
  1031.  
  1032.   local sb = UI.StringBuffer(self.width)
  1033.   local x = 1
  1034.   for k,col in ipairs(self.columns) do
  1035.     local width = col[3] + 1
  1036.     sb:insert(col[1], x)
  1037.     x = x + width
  1038.   end
  1039.   console:write(self.x, self.y, sb:get(), colors.blue)
  1040. end
  1041.  
  1042. function UI.Grid:calculateWidth()
  1043.   -- gutters on each side
  1044.   local width = 2
  1045.   for _,col in pairs(self.columns) do
  1046.     width = width + col[3] + 1
  1047.   end
  1048.   return width - 1
  1049. end
  1050.  
  1051. function UI.Grid:drawRows()
  1052.  
  1053.   local function sortM(a, b)
  1054.     return a[self.sortColumn] < b[self.sortColumn]
  1055.   end
  1056.  
  1057.   local function inverseSortM(a, b)
  1058.     return a[self.sortColumn] > b[self.sortColumn]
  1059.   end
  1060.  
  1061.   local sortMethod
  1062.   if self.sortColumn then
  1063.     sortMethod = sortM
  1064.     if self.inverseSort then
  1065.       sortMethod = inverseSortM
  1066.     end
  1067.   end
  1068.  
  1069.   if self.index > Util.size(self.t) then
  1070.     local newIndex = Util.size(self.t)
  1071.     if newIndex <= 0 then
  1072.       newIndex = 1
  1073.     end
  1074.     self:setIndex(newIndex)
  1075.     return
  1076.   end
  1077.  
  1078.   local startRow = self:getStartRow()
  1079.   local y = self.y
  1080.   local rowCount = 0
  1081.   local sb = UI.StringBuffer(self.width)
  1082.  
  1083.   if not self.disableHeader then
  1084.     y = y + 1
  1085.   end
  1086.  
  1087.   local index = 1
  1088.   for _,row in Util.spairs(self.t, sortMethod) do
  1089.     if index >= startRow then
  1090.       sb:clear()
  1091.       if index >= startRow + self.pageSize then
  1092.         break
  1093.       end
  1094.  
  1095.       if not console.isColor then
  1096.         if index == self.index and self.selectable then
  1097.           sb:insert('>', 0)
  1098.         end
  1099.       end
  1100.  
  1101.       local x = 1
  1102.       for _,col in pairs(self.columns) do
  1103.  
  1104.         local value = row[col[2]]
  1105.         if value then
  1106.           sb:insert(string.sub(value, 1, col[3]), x)
  1107.         end
  1108.  
  1109.         x = x + col[3] + 1
  1110.       end
  1111.  
  1112.       local selected = index == self.index and self.selectable
  1113.       if selected then
  1114.         self:setSelected(row)
  1115.       end
  1116.  
  1117.       term.setTextColor(self:getRowTextColor(row, selected))
  1118.       console:write(self.x, y, sb:get(), self:getRowBackgroundColor(row, selected))
  1119.  
  1120.       y = y + 1
  1121.       rowCount = rowCount + 1
  1122.     end
  1123.     index = index + 1
  1124.   end
  1125.  
  1126.   if rowCount < self.pageSize then
  1127.     console:clearArea(self.x, y, self.width, self.pageSize-rowCount, self.backgroundColor)
  1128.   end
  1129.   term.setTextColor(colors.white)
  1130. end
  1131.  
  1132. function UI.Grid:getRowTextColor(row, selected)
  1133.   if selected then
  1134.     return self.textSelectedColor
  1135.   end
  1136.   return self.textColor
  1137. end
  1138.  
  1139. function UI.Grid:getRowBackgroundColor(row, selected)
  1140.   if selected then
  1141.     return self.backgroundSelectedColor
  1142.   end
  1143.   return self.backgroundColor
  1144. end
  1145.  
  1146. function UI.Grid:getIndex(index)
  1147.   return self.index
  1148. end
  1149.  
  1150. function UI.Grid:setIndex(index)
  1151.   if self.index ~= index then
  1152.     if index < 1 then
  1153.       index = 1
  1154.     end
  1155.     self.index = index
  1156.     self:drawRows()
  1157.   end
  1158. end
  1159.  
  1160. function UI.Grid:getStartRow()
  1161.   return math.floor((self.index - 1)/ self.pageSize) * self.pageSize + 1
  1162. end
  1163.  
  1164. function UI.Grid:getPage()
  1165.   return math.floor(self.index / self.pageSize) + 1
  1166. end
  1167.  
  1168. function UI.Grid:getPageCount()
  1169.   local tableSize = Util.size(self.t)
  1170.   local pc = math.floor(tableSize / self.pageSize)
  1171.   if tableSize % self.pageSize > 0 then
  1172.     pc = pc + 1
  1173.   end
  1174.   return pc
  1175. end
  1176.  
  1177. function UI.Grid:setPage(pageNo)
  1178.   -- 1 based paging
  1179.   self:setIndex((pageNo-1) * self.pageSize + 1)
  1180. end
  1181.  
  1182. function UI.Grid:keyHandler(ch)
  1183.  
  1184.   if ch == 'j' or ch == 'down' then
  1185.     self:setIndex(self.index + 1)
  1186.   elseif ch == 'k' or ch == 'up' then
  1187.     self:setIndex(self.index - 1)
  1188.   elseif ch == 'h' then
  1189.     self:setIndex(self.index - self.pageSize)
  1190.   elseif ch == 'l' then
  1191.     self:setIndex(self.index + self.pageSize)
  1192.   elseif ch == 'home' then
  1193.     self:setIndex(1)
  1194.   elseif ch == 'end' then
  1195.     self:setIndex(Util.size(self.t))
  1196.   elseif ch == 'r' then
  1197.     self:draw()
  1198.   elseif ch == 's' then
  1199.     self:setInverseSort(not self.inverseSort)
  1200.   else
  1201.     return false
  1202.   end
  1203.   return true
  1204. end
  1205.  
  1206. --[[-- ScrollingGrid  --]]--
  1207. UI.ScrollingGrid = class.class(UI.Grid)
  1208. function UI.ScrollingGrid:init(args)
  1209.   local defaults = {
  1210.     scrollOffset = 1
  1211.   }
  1212.   UI.setProperties(self, defaults)
  1213.   UI.Grid.init(self, args)
  1214. end
  1215.  
  1216. function UI.ScrollingGrid:drawRows()
  1217.   UI.Grid.drawRows(self)
  1218.   self:drawScrollbar()
  1219. end
  1220.  
  1221. function UI.ScrollingGrid:drawScrollbar()
  1222.   local ts = Util.size(self.t)
  1223.   if ts > self.pageSize then
  1224.     term.setBackgroundColor(self.backgroundColor)
  1225.     local sbSize = self.pageSize - 2
  1226.     local sa = ts -- - self.pageSize
  1227.     sa = self.pageSize / sa
  1228.     sa = math.floor(sbSize * sa)
  1229.     if sa < 1 then
  1230.       sa = 1
  1231.     end
  1232.     if sa > sbSize then
  1233.       sa = sbSize
  1234.     end
  1235.     local sp = ts-self.pageSize
  1236.     sp = self.scrollOffset / sp
  1237.     sp = math.floor(sp * (sbSize-sa + 0.5))
  1238. --console:reset()
  1239. --print('sb: ' .. sbSize .. ' sa:' .. sa .. ' sp:' .. sp)
  1240. --read()
  1241.  
  1242.     local x = self.x + self.width-1
  1243.     if self.scrollOffset > 1 then
  1244.       console:write(x, self.y + 1, '^')
  1245.     else
  1246.       console:write(x, self.y + 1, ' ')
  1247.     end
  1248.     local row = 0
  1249.     for i = 0, sp - 1 do
  1250.       console:write(x, self.y + row+2, '|')
  1251.       row = row + 1
  1252.     end
  1253.     for i = 1, sa do
  1254.       console:write(x, self.y + row+2, '#')
  1255.       row = row + 1
  1256.     end
  1257.     for i = row, sbSize do
  1258.       console:write(x, self.y + row+2, '|')
  1259.       row = row + 1
  1260.     end
  1261.     if self.scrollOffset + self.pageSize - 1 < Util.size(self.t) then
  1262.       console:write(x, self.y + self.pageSize, 'v')
  1263.     else
  1264.       console:write(x, self.y + self.pageSize, ' ')
  1265.     end
  1266.   end
  1267. end
  1268.  
  1269. function UI.ScrollingGrid:getStartRow()
  1270.   local ts = Util.size(self.t)
  1271.   if ts < self.pageSize then
  1272.     self.scrollOffset = 1
  1273.   end
  1274.   return self.scrollOffset
  1275. end
  1276.  
  1277. function UI.ScrollingGrid:setIndex(index)
  1278.   if index < self.scrollOffset then
  1279.     self.scrollOffset = index
  1280.   elseif index - (self.scrollOffset - 1) > self.pageSize then
  1281.     self.scrollOffset = index - self.pageSize + 1
  1282.   end
  1283.  
  1284.   if self.scrollOffset < 1 then
  1285.     self.scrollOffset = 1
  1286.   else
  1287.     local ts = Util.size(self.t)
  1288.     if self.pageSize + self.scrollOffset > ts then
  1289.       self.scrollOffset = ts - self.pageSize + 1
  1290.     end
  1291.   end
  1292.   UI.Grid.setIndex(self, index)
  1293. end
  1294.  
  1295. --[[-- Menu  --]]--
  1296. UI.Menu = class.class(UI.Grid)
  1297.  
  1298. function UI.Menu:init(args)
  1299.   local defaults = {
  1300.     disableHeader = true,
  1301.     columns = { { 'Prompt', 'prompt', 20 } },
  1302.     t = args['menuItems'],
  1303.     width = 1
  1304.   }
  1305.   UI.Grid.init(self, defaults)
  1306.   UI.setProperties(self, args)
  1307.   self.pageSize = #self.menuItems
  1308.   for _,v in pairs(self.t) do
  1309.     if string.len(v.prompt) > self.width then
  1310.       self.width = string.len(v.prompt)
  1311.     end
  1312.   end
  1313.   self.width = self.width + 2
  1314. end
  1315.  
  1316. function UI.Menu:center()
  1317.   local width = 0
  1318.   for _,v in pairs(self.menuItems) do
  1319.     local len = string.len(v.prompt)
  1320.     if len > width then
  1321.       width = len
  1322.     end
  1323.   end
  1324.   self.x = (console.width - width) / 2
  1325.   self.y = (console.height - #self.menuItems) / 2
  1326. end
  1327.  
  1328. function UI.Menu:keyHandler(ch)
  1329.   if ch and self.menuItems[tonumber(ch)] then
  1330.     self.menuItems[tonumber(ch)].action()
  1331.   elseif ch == 'enter' then
  1332.     self.menuItems[self.index].action()
  1333.   else
  1334.     return UI.Grid.keyHandler(self, ch)
  1335.   end
  1336.   return true
  1337. end
  1338.  
  1339. --[[-- ViewportConsole  --]]--
  1340. UI.ViewportConsole = class.class()
  1341. function UI.ViewportConsole:init(args)
  1342.   local defaults = {
  1343.     x = 1,
  1344.     y = 1,
  1345.     width = console.width,
  1346.     height = console.height,
  1347.     offset = 0,
  1348.     vpx = 1,
  1349.     vpy = 1,
  1350.     vpHeight = console.height
  1351.   }
  1352.   UI.setProperties(self, defaults)
  1353.   UI.setProperties(self, args)
  1354. end
  1355.  
  1356. function UI.ViewportConsole:setCursorPos(x, y)
  1357.   self.vpx = x
  1358.   self.vpy = y
  1359.   if self.vpy > self.height then
  1360.     self.height = self.vpy
  1361.   end
  1362. end
  1363.  
  1364. function UI.ViewportConsole:reset(bg)
  1365.   console:clearArea(self.x, self.y, self.width, self.vpHeight, bg)
  1366.   self:setCursorPos(1, 1)
  1367. end
  1368.  
  1369. function UI.ViewportConsole:clearArea(x, y, width, height, bg)
  1370.   if bg then
  1371.     term.setBackgroundColor(bg)
  1372.   end
  1373.   y = y - self.offset
  1374.   for i = 1, height do
  1375.     if y > 0 and y <= self.vpHeight then
  1376.       term.setCursorPos(x, self.y + y - 1)
  1377.       term.clearLine()
  1378.     end
  1379.     y = y + 1
  1380.   end
  1381. end
  1382.  
  1383. function UI.ViewportConsole:pr(text, bg)
  1384.   self:write(self.vpx, self.vpy, text, bg)
  1385.   self:setCursorPos(1, self.vpy + 1)
  1386. end
  1387.  
  1388. function UI.ViewportConsole:write(x, y, text, bg)
  1389.   y = y - self.offset
  1390.   if y > 0 and y <= self.vpHeight then
  1391.     console:write(self.x + x - 1, self.y + y - 1, text, bg)
  1392.   end
  1393. end
  1394.  
  1395. function UI.ViewportConsole:wrappedPrint(text, indent, len, bg)
  1396.   indent = indent or 1
  1397.   len = len or self.width - indent
  1398.   for k,v in pairs(Util.WordWrap(text, len+1)) do
  1399.     self:write(indent, self.vpy, v, bg)
  1400.     self.vpy = self.vpy + 1
  1401.   end
  1402. end
  1403.  
  1404. function UI.ViewportConsole:wrappedWrite(x, y, text, len, bg)
  1405.   for k,v in pairs(Util.WordWrap(text, len)) do
  1406.     self:write(x, y, v, bg)
  1407.     y = y + 1
  1408.   end
  1409.   return y
  1410. end
  1411.  
  1412. function UI.ViewportConsole:setPage(pageNo)
  1413.   self:setOffset((pageNo-1) * self.vpHeight + 1)
  1414. end
  1415.  
  1416. function UI.ViewportConsole:setOffset(offset)
  1417.   self.offset = math.max(0, math.min(math.max(0, offset), self.height-self.vpHeight))
  1418.   self:draw()
  1419. end
  1420.  
  1421. function UI.ViewportConsole:draw()
  1422. end
  1423.  
  1424. function UI.ViewportConsole:keyHandler(ch)
  1425.  
  1426.   if ch == 'j' or ch == 'down' then
  1427.     self:setOffset(self.offset + 1)
  1428.   elseif ch == 'k' or ch == 'up' then
  1429.     self:setOffset(self.offset - 1)
  1430.   elseif ch == 'home' then
  1431.     self:setOffset(0)
  1432.   elseif ch == 'end' then
  1433.     self:setOffset(self.height-self.vpHeight)
  1434.   elseif ch == 'h' then
  1435.     self:setPage(
  1436.       math.floor((self.offset - self.vpHeight) / self.vpHeight))
  1437.   elseif ch == 'l' then
  1438.     self:setPage(
  1439.       math.floor((self.offset + self.vpHeight) / self.vpHeight) + 1)
  1440.   else
  1441.     return false
  1442.   end
  1443.   return true
  1444. end
  1445.  
  1446. --[[-- ScrollingText  --]]--
  1447. UI.ScrollingText = class.class()
  1448. function UI.ScrollingText:init(args)
  1449.   local defaults = {
  1450.     x = 1,
  1451.     y = 1,
  1452.     height = console.height,
  1453.     backgroundColor = colors.black,
  1454.     width = console.width,
  1455.     buffer = { }
  1456.   }
  1457.   UI.setProperties(self, defaults)
  1458.   UI.setProperties(self, args)
  1459. end
  1460.  
  1461. function UI.ScrollingText:write(text)
  1462.   if #self.buffer+1 >= self.height then
  1463.     table.remove(self.buffer, 1)
  1464.   end
  1465.   table.insert(self.buffer, text)
  1466.   self:draw()
  1467. end
  1468.  
  1469. function UI.ScrollingText:clear()
  1470.   self.buffer = { }
  1471.   console:clearArea(self.x, self.y, self.width, self.height, self.backgroundColor)
  1472. end
  1473.  
  1474. function UI.ScrollingText:draw()
  1475.   for k,text in ipairs(self.buffer) do
  1476.     console:write(self.x, self.y + k, widthify(text, self.width), self.backgroundColor)
  1477.   end
  1478. end
  1479.  
  1480. --[[-- TitleBar  --]]--
  1481. UI.TitleBar = class.class()
  1482. function UI.TitleBar:init(args)
  1483.   local defaults = {
  1484.     x = 1,
  1485.     y = 1,
  1486.     backgroundColor = colors.brown,
  1487.     width = console.width,
  1488.     title = ''
  1489.   }
  1490.   UI.setProperties(self, defaults)
  1491.   UI.setProperties(self, args)
  1492. end
  1493.  
  1494. function UI.TitleBar:draw()
  1495.   console:clearArea(self.x, self.y, self.width, 1, self.backgroundColor)
  1496.   local centered = (self.width -#self.title) / 2
  1497.   console:write(self.x + centered, self.y, self.title)
  1498.   term.setBackgroundColor(colors.black)
  1499. end
  1500.  
  1501. --[[-- StatusBar  --]]--
  1502. UI.StatusBar = class.class(UI.Grid)
  1503. function UI.StatusBar:init(args)
  1504.   local defaults = {
  1505.     selectable = false,
  1506.     disableHeader = true,
  1507.     y = console.height,
  1508.     backgroundColor = colors.gray,
  1509.     width = console.width,
  1510.     t = {{}}
  1511.   }
  1512.   UI.setProperties(defaults, args)
  1513.   UI.Grid.init(self, defaults)
  1514.   if self.values then
  1515.     self:setValues(self.values)
  1516.   end
  1517. end
  1518.  
  1519. function UI.StatusBar:setValues(values)
  1520.   self.t[1] = values
  1521. end
  1522.  
  1523. function UI.StatusBar:setValue(name, value)
  1524.   self.t[1][name] = value
  1525. end
  1526.  
  1527. function UI.StatusBar:getValue(name)
  1528.   return self.t[1][name]
  1529. end
  1530.  
  1531. function UI.StatusBar:getColumnWidth(name)
  1532.   for _,v in pairs(self.columns) do
  1533.     if v[2] == name then
  1534.       return v[3]
  1535.     end
  1536.   end
  1537. end
  1538.  
  1539. function UI.StatusBar:setColumnWidth(name, width)
  1540.   for _,v in pairs(self.columns) do
  1541.     if v[2] == name then
  1542.       v[3] = width
  1543.       break
  1544.     end
  1545.   end
  1546. end
  1547.  
  1548. --[[-- Form  --]]--
  1549. UI.Form = class.class()
  1550. UI.Form.D = { -- display
  1551.  
  1552.   static = {
  1553.     draw = function(field)
  1554.       console:write(field.x, field.y, widthify(field.value, field.width), colors.black)
  1555.     end
  1556.   },
  1557.  
  1558.   entry = {
  1559.  
  1560.     draw = function(field)
  1561.       console:write(field.x, field.y, widthify(field.value, field.width), colors.gray)
  1562.     end,
  1563.  
  1564.     updateCursor = function(field)
  1565.       term.setCursorPos(field.x + field.pos, field.y)
  1566.     end,
  1567.  
  1568.     focus = function(field)
  1569.       term.setCursorBlink(true)
  1570.       if not field.pos then
  1571.         field.pos = #field.value
  1572.       end
  1573.       field.display.updateCursor(field)
  1574.     end,
  1575.  
  1576.     loseFocus = function(field)
  1577.       term.setCursorBlink(false)
  1578.     end,
  1579. --[[
  1580.   A few lines below from theoriginalbit
  1581.   http://www.computercraft.info/forums2/index.php?/topic/16070-read-and-limit-length-of-the-input-field/
  1582. --]]
  1583.     keyHandler = function(form, field, ch)
  1584.       if ch == 'enter' then
  1585.         form:selectNextField()
  1586.         -- self.accept(self)
  1587.       elseif ch == 'left' then
  1588.         if field.pos > 0 then
  1589.           field.pos = math.max(field.pos-1, 0)
  1590.           field.display.updateCursor(field)
  1591.         end
  1592.       elseif ch == 'right' then
  1593.         local input = field.value
  1594.         if field.pos < #input then
  1595.           field.pos = math.min(field.pos+1, #input)
  1596.           field.display.updateCursor(field)
  1597.         end
  1598.       elseif ch == 'home' then
  1599.         field.pos = 0
  1600.         field.display.updateCursor(field)
  1601.       elseif ch == 'end' then
  1602.         field.pos = #field.value
  1603.         field.display.updateCursor(field)
  1604.       elseif ch == 'backspace' then
  1605.         if field.pos > 0 then
  1606.           local input = field.value
  1607.           field.value = input:sub(1, field.pos-1) .. input:sub(field.pos+1)
  1608.           field.pos = field.pos - 1
  1609.           field.display.draw(field)
  1610.           field.display.updateCursor(field)
  1611.         end
  1612.       elseif ch == 'delete' then
  1613.         local input = field.value
  1614.         if field.pos < #input then
  1615.           field.value = input:sub(1, field.pos)..input:sub(field.pos+2)
  1616.           field.display.draw(field)
  1617.           field.display.updateCursor(field)
  1618.         end
  1619.       elseif #ch == 1 then
  1620.         local input = field.value
  1621.  
  1622.         if #input < field.width then
  1623.           field.value = input:sub(1, field.pos) .. ch .. input:sub(field.pos+1)
  1624.           field.pos = field.pos + 1
  1625.           field.display.draw(field)
  1626.           field.display.updateCursor(field)
  1627.         end
  1628.       else
  1629.         return false
  1630.       end
  1631.       return true
  1632.     end
  1633.   },
  1634.   button = {
  1635.     draw = function(field, focused)
  1636.       local bg = colors.brown
  1637.       if focused then
  1638.         bg = colors.green
  1639.       end
  1640.       console:clearArea(field.x, field.y, field.width, 1, bg)
  1641.       console:write(
  1642.               field.x + math.ceil(field.width/2) - math.ceil(#field.text/2),
  1643.               field.y,
  1644.               tostring(field.text))
  1645.     end,
  1646.     focus = function(field)
  1647.       field.display.draw(field, true)
  1648.     end,
  1649.     loseFocus = function(field)
  1650.       field.display.draw(field, false)
  1651.     end,
  1652.     keyHandler = function(form, field, ch)
  1653.       if ch == 'enter' then
  1654.         form.cancel(form)
  1655.       else
  1656.         return false
  1657.       end
  1658.       return true
  1659.     end
  1660.   }
  1661. }
  1662.  
  1663. UI.Form.V = { -- validation
  1664.   number = function(value)
  1665.     return type(value) == 'number'
  1666.   end
  1667. }
  1668.  
  1669. UI.Form.T = { -- data types
  1670.   number = function(value)
  1671.     return tonumber(value)
  1672.   end
  1673. }
  1674.  
  1675. function UI.Form:init(args)
  1676.   local defaults = {
  1677.     values = {},
  1678.     fields = {},
  1679.     columns = {
  1680.       { 'Name', 'name', 20 },
  1681.       { 'Values', 'value', 20 }
  1682.     },
  1683.     x = 1,
  1684.     y = 1,
  1685.     nameWidth = 20,
  1686.     valueWidth = 20,
  1687.     accept = function() end,
  1688.     cancel = function() end
  1689.   }
  1690.   UI.setProperties(self, defaults)
  1691.   UI.setProperties(self, args)
  1692.  
  1693. end
  1694.  
  1695. function UI.Form:setValues(values)
  1696.   self.values = values
  1697.  
  1698.   -- store the value in the field in case the user cancels entry
  1699.   for k,field in pairs(self.fields) do
  1700.     if field.key then
  1701.       field.value = self.values[field.key]
  1702.       if not field.value then
  1703.         field.value = ''
  1704.       end
  1705.     end
  1706.   end
  1707. end
  1708.  
  1709. function UI.Form:draw()
  1710.  
  1711.   local y = self.y
  1712.   for k,field in ipairs(self.fields) do
  1713.  
  1714.     console:write(self.x, y, field.name, colors.black)
  1715.  
  1716.     field.x = self.x + 1 + self.nameWidth
  1717.     field.y = y
  1718.     field.width = self.valueWidth
  1719.  
  1720.     if not self.fieldNo and field.display.focus then
  1721.       self.fieldNo = k
  1722.     end
  1723.  
  1724.     field.display.draw(field, k == self.fieldNo)
  1725.  
  1726.     y = y + 1
  1727.   end
  1728.  
  1729.   local field = self:getCurrentField()
  1730.   field.display.focus(field)
  1731. end
  1732.  
  1733. function UI.Form:getCurrentField()
  1734.   if self.fieldNo then
  1735.     return self.fields[self.fieldNo]
  1736.   end
  1737. end
  1738.  
  1739. function UI.Form:selectField(index)
  1740.   local field = self:getCurrentField()
  1741.   if field then
  1742.     field.display.loseFocus(field)
  1743.   end
  1744.  
  1745.   self.fieldNo = index
  1746.  
  1747.   field = self:getCurrentField()
  1748.   field.display.focus(field)
  1749. end
  1750.  
  1751. function UI.Form:selectFirstField()
  1752.   for k,field in ipairs(self.fields) do
  1753.     if field.display.focus then
  1754.       self:selectField(k)
  1755.       break
  1756.     end
  1757.   end
  1758. end
  1759.  
  1760. function UI.Form:selectPreviousField()
  1761.   for k = self.fieldNo - 1, 1, -1 do
  1762.     local field = self.fields[k]
  1763.     if field.display.focus then
  1764.       self:selectField(k)
  1765.       break
  1766.     end
  1767.   end
  1768. end
  1769.  
  1770. function UI.Form:selectNextField()
  1771.   for k = self.fieldNo + 1, #self.fields do
  1772.     local field = self.fields[k]
  1773.     if field.display.focus then
  1774.       self:selectField(k)
  1775.       break
  1776.     end
  1777.   end
  1778. end
  1779.  
  1780. function UI.Form:keyHandler(ch)
  1781.  
  1782.   local field = self:getCurrentField()
  1783.   if field.display.keyHandler(self, field, ch) then
  1784.     return true
  1785.   end
  1786.  
  1787.   if ch == 'down' then
  1788.     self:selectNextField()
  1789.   elseif ch == 'up' then
  1790.     self:selectPreviousField()
  1791.   else
  1792.     return false
  1793.   end
  1794.  
  1795.   return true
  1796. end
  1797.  
  1798. --[[-- Spinner  --]]--
  1799. UI.Spinner = class.class()
  1800. function UI.Spinner:init(args)
  1801.   local defaults = {
  1802.     timeout = .095,
  1803.     x = 1,
  1804.     y = 1,
  1805.     c = os.clock(),
  1806.     spinIndex = 0,
  1807.     spinSymbols = { '-', '/', '|', '\\' }
  1808.   }
  1809.   UI.setProperties(self, defaults)
  1810.   UI.setProperties(self, args)
  1811. end
  1812.  
  1813. function UI.Spinner:spin()
  1814.   local cc = os.clock()
  1815.   if cc > self.c + self.timeout then
  1816.     term.setCursorPos(self.x, self.y)
  1817.     term.write(self.spinSymbols[self.spinIndex % #self.spinSymbols + 1])
  1818.     self.spinIndex = self.spinIndex + 1
  1819.     self.c = cc
  1820.     os.sleep(0)
  1821.   end
  1822. end
  1823.  
  1824. function UI.Spinner:getCursorPos()
  1825.   self.x, self.y = term.getCursorPos()
  1826. end
  1827.  
  1828. -----------------------------------------------
  1829. -- UIL2 ?
  1830.  
  1831. function UI.messageBox(text)
  1832.   local w = console.width - 4
  1833.   local h = console.height - 4
  1834.   local x = 3
  1835.   local y = 3
  1836.   console:clearArea(x, y, w-1, h, colors.red)
  1837.  
  1838.   console:wrappedWrite(x+2, y+2, text, w-4, colors.red)
  1839.   console:write(x+1, y, string.rep('oh', w/2), colors.orange)
  1840.   console:write(x+1, y+h-1, string.rep('no', w/2), colors.orange)
  1841.   for i = y, y + h - 1 do
  1842.     console:write(x, i, 'O', colors.orange)
  1843.     console:write(x+w-1, i, 'H', colors.orange)
  1844.   end
  1845.   console:wrappedWrite(x+2, y+h-3, 'Press enter to continue', w-4, colors.red)
  1846.   read()
  1847.   term.setBackgroundColor(colors.black)
  1848. end
  1849.  
  1850.  
  1851.  
  1852. local console = UI.getConsole()
  1853.  
  1854. --[[ -- MethodsPage  -- ]] --
  1855. peripheralsPage = UI.Page({
  1856.   titleBar = UI.TitleBar({
  1857.     title = 'Peripheral Viewer v0.0001'
  1858.   }),
  1859.   grid = UI.ScrollingGrid({
  1860.     columns = {
  1861.       { 'Type', 'type', console.width-10 },
  1862.       { 'Side', 'side', 8 }
  1863.     },  
  1864.     sortColumn = 'type',
  1865.     pageSize = console.height-3,
  1866.     width = console.width,
  1867.     y = 2,
  1868.     x = 1
  1869.   }),
  1870.   statusBar = UI.StatusBar({
  1871.     columns = {
  1872.       { '', 'msg', console.width },
  1873.     },  
  1874.     x = 1,
  1875.     backgroundColor = colors.blue
  1876.   })
  1877. })
  1878.  
  1879. function peripheralsPage:draw()
  1880.   local sides = peripheral.getNames()
  1881.   local t = { }
  1882.   for _,side in pairs(sides) do
  1883.     table.insert(t, {
  1884.       type = peripheral.getType(side),
  1885.       side = side
  1886.     })
  1887.   end
  1888.  
  1889.   self.titleBar:draw()
  1890.   self.grid:setTable(t)
  1891.   self.grid:draw()
  1892.   self.statusBar:setValue('msg', 'Select peripheral')
  1893.   self.statusBar:draw()
  1894. end
  1895.  
  1896. function peripheralsPage:keyHandler(ch)
  1897.   if ch == 'q' then
  1898.     Event.exitPullEvents()
  1899.   elseif ch == 'enter' then
  1900.     methodsPage.selected = self.grid:getSelected()
  1901.     methodsPage.grid.index = 1
  1902.     methodsPage.grid.scrollOffset = 1
  1903.     pager:setPageRaw(methodsPage)
  1904.   else
  1905.     return  self.grid:keyHandler(ch)
  1906.   end
  1907.   return false
  1908. end
  1909.  
  1910. --[[ -- MethodsPage  -- ]] --
  1911. methodsPage = UI.Page({
  1912.   titleBar = UI.TitleBar(),
  1913.   grid = UI.ScrollingGrid({
  1914.     columns = {
  1915.       { 'Name', 'name', console.width }
  1916.     },  
  1917.     sortColumn = 'name',
  1918.     pageSize = 5,
  1919.     width = console.width,
  1920.     y = 2,
  1921.     x = 1
  1922.   }),
  1923.   viewportConsole = UI.ViewportConsole({
  1924.     y = 8,
  1925.     vpHeight = console.height-8
  1926.   }),
  1927.   statusBar = UI.StatusBar({
  1928.     columns = {
  1929.       { '', 'msg', console.width },
  1930.     },  
  1931.     x = 1,
  1932.     backgroundColor = colors.blue
  1933.   })
  1934. })
  1935.  
  1936. function methodsPage:enable()
  1937.   self.extendedInfo = false
  1938.   local p = peripheral.wrap(self.selected.side)
  1939.   if not p.getAdvancedMethodsData then
  1940.     local t = { }
  1941.     for name,f in pairs(p) do
  1942.       table.insert(t, { name = name })
  1943.     end
  1944.     self.grid.pageSize = console.height - 3
  1945.     self.grid.t = t
  1946.   else
  1947.     self.grid.t = p.getAdvancedMethodsData()
  1948.     self.grid.pageSize = 5
  1949.     self.extendedInfo = true
  1950.   end
  1951.  
  1952.   if self.extendedInfo then
  1953.     self.titleBar.title = self.selected.type
  1954.   else
  1955.     self.titleBar.title = self.selected.type .. ' (no ext info)'
  1956.   end
  1957.  
  1958.   self.statusBar:setValue('msg', 'q to return')
  1959. end
  1960.  
  1961. function methodsPage:draw()
  1962.   self.titleBar:draw()
  1963.   self.grid:draw()
  1964.   if self.extendedInfo then
  1965. if self.grid:getSelected() then
  1966.     drawMethodInfo(self.viewportConsole, self.grid:getSelected())
  1967. end
  1968.   end
  1969.   self.statusBar:draw()
  1970. end
  1971.  
  1972. function methodsPage:keyHandler(ch)
  1973.   if ch == 'q' then
  1974.     pager:setPageRaw(peripheralsPage)
  1975.   elseif ch == 'enter' then
  1976.     if self.extendedInfo then
  1977.       pager:setPageRaw(methodDetailsPage)
  1978.     end
  1979.   elseif self.grid:keyHandler(ch) then
  1980.     if self.extendedInfo then
  1981.       drawMethodInfo(self.viewportConsole, self.grid:getSelected())
  1982.     end
  1983.   end
  1984. end
  1985.  
  1986. --[[ -- MethodDetailsPage  -- ]] --
  1987. methodDetailsPage = UI.Page({
  1988.   viewportConsole = UI.ViewportConsole({
  1989.     y = 1,
  1990.     height = console.height-1,
  1991.     vpHeight = console.height-1
  1992.   }),
  1993.   statusBar = UI.StatusBar({
  1994.     columns = {
  1995.       { '', 'msg', console.width }
  1996.     },  
  1997.     x = 1,
  1998.     backgroundColor = colors.blue
  1999.   })
  2000. })
  2001.  
  2002. function methodDetailsPage:enable()
  2003.   self.viewportConsole.offset = 0
  2004.   self.viewportConsole.height = console.height-1
  2005.   self.statusBar:setValue('msg', 'enter to return')
  2006. end
  2007.  
  2008. function methodDetailsPage:draw()
  2009.   drawMethodInfo(self.viewportConsole, methodsPage.grid:getSelected())
  2010.   self.statusBar:draw()
  2011. end
  2012.  
  2013. function methodDetailsPage:keyHandler(ch)
  2014.   if ch == 'enter' or ch == 'q' then
  2015.     pager:setPreviousPage()
  2016.   elseif self.viewportConsole:keyHandler(ch) then
  2017.     drawMethodInfo(self.viewportConsole, methodsPage.grid:getSelected())
  2018.   end
  2019. end
  2020.  
  2021. --[[ -- Common logic  -- ]] --
  2022. function drawMethodInfo(c, method)
  2023.  
  2024.   c:reset(colors.brown)
  2025.  
  2026.   if method.description then
  2027.     c:wrappedPrint(method.description)
  2028.     c:pr('')
  2029.   end
  2030.  
  2031.   local str = method.name .. '('
  2032.   for k,arg in ipairs(method.args) do
  2033.     str = str .. arg.name
  2034.     if k < #method.args then
  2035.       str = str .. ', '
  2036.     end
  2037.   end
  2038.   c:wrappedPrint(str .. ')')
  2039.  
  2040.   local sb = UI.StringBuffer(0)
  2041.   if #method.returnTypes > 0 then
  2042.     sb:clear()
  2043.     sb:append('Returns: ')
  2044.     for k,ret in ipairs(method.returnTypes) do
  2045.       sb:append(ret)
  2046.       if k < #method.returnTypes then
  2047.         sb:append(', ')
  2048.       end
  2049.     end
  2050.     c:pr(sb:get())
  2051.   end
  2052.  
  2053.   if #method.args > 0 then
  2054.     for _,arg in ipairs(method.args) do
  2055.       c:pr('')
  2056.       c:wrappedPrint(arg.name .. ': ' .. arg.description)
  2057.       c:pr('')
  2058.       c:pr('optional nullable type    vararg')
  2059.       c:pr('-------- -------- ----    ------')
  2060.       sb:clear()
  2061.       sb:insert(tostring(arg.optional), 0)
  2062.       sb:insert(tostring(arg.nullable), 9)
  2063.       sb:insert(arg.type, 18)
  2064.       sb:insert(tostring(arg.vararg), 26)
  2065.       c:pr(sb:get())
  2066.     end
  2067.   end
  2068.  
  2069.   term.setBackgroundColor(colors.black)
  2070.   return y
  2071. end
  2072.  
  2073. --[[ -- Startup logic  -- ]] --
  2074. pager = UI.Pager()
  2075. pager:setPageRaw(peripheralsPage)
  2076.  
  2077. Logger.disableLogging()
  2078. Event.pullEvents()
  2079. console:reset()
Add Comment
Please, Sign In to add comment