SHARE
TWEET

InfoPanel IT 1.7.10

MoonlightOwl Jan 17th, 2015 (edited) 435 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- ИнфоПанель 1.1
  2. -- Информационное табло, создано специально для сервера
  3. --            computercraft.ru - IT 1.7.10
  4. -- Автор идеи: AlexCC
  5. -- Создатели: Totoro
  6. -- (c) 01.2015
  7.  
  8. local unicode = require('unicode')
  9. local event = require('event')
  10. local fs = require('filesystem')
  11. local computer = require('computer')
  12. local com = require('component')
  13. local term = require('term')
  14. local gpu = com.gpu
  15.  
  16. function trytoload(name)
  17.   if com.isAvailable(name) then
  18.     return com.getPrimary(name)
  19.   else
  20.     return nil
  21.   end
  22. end
  23.  
  24. -- =========================== V A R I A B L E S =========================== --
  25. -- масштаб текста
  26. local defaultRatio = 8
  27. local textRatio = defaultRatio
  28. -- размер и разрешение монитора
  29. local screenSize = {width = 6, height = 3}
  30. local screenRes = {width = 48, height = 12}
  31. local oldRes = {width = 0, height = 0}
  32. -- базовые цвета оформления
  33. local basicforeground = 0xFFFFFF
  34. local basicbackground = 0x000000
  35. local linksbackground = 0x111111
  36. -- поля
  37. local margin = 1
  38. -- простой перед сбросом на главную (сек)
  39. local downtime = 60 * 5
  40. local lasttime = 0
  41.  
  42. -- список кнопок-разделов
  43. -- pages = {{file=..., name=...}, ...}
  44. local pages = {}
  45. local rows = {0}
  46.  
  47. -- рендер текущей страницы
  48. --                         блок                       и т.д.
  49. -- render = {{{x=..., color=..., text=..., link=...},  ...  }, -- строка
  50. --           {{...}, {...}, {...}},                            -- другая строка
  51. --           {...}, ...}                                       -- и т.д.
  52. local render = {}
  53.  
  54.  
  55. -- ========================== L O A D   F I L E S ========================== --
  56. -- загружаем настройки и таблицу страниц
  57. function loadConfig(configfile)
  58.   file = io.open(configfile, "r")
  59.   if file ~= nil then
  60.     pages = {}
  61.     for line in file:lines() do
  62.       -- если строка не пуста и не комментарий
  63.       if string.len(line) ~= 0 and line:sub(1,1) ~= "#" then
  64.         key, data = line:match("(.+)=(.+)")
  65.         -- читаем масштаб текста
  66.         if key == "textScale" then
  67.           value = tonumber(data)
  68.           if value ~= nil then textRatio = defaultRatio/value end
  69.         -- читает задержку перед сбросом
  70.         elseif key == 'downtime' then
  71.           value = tonumber(data)
  72.           if value ~= nil then downtime = value end
  73.         -- читаем размер полей
  74.         elseif key == 'margin' then
  75.           value = tonumber(data)
  76.           if value ~= nil then margin = value end
  77.         -- читаем основной цвет фона
  78.         elseif key == "background" then
  79.           value = tonumber(data:sub(2,-1), 16)
  80.           if value ~= nil then basicbackground = value end
  81.         -- читаем основной цвет текста
  82.         elseif key == "foreground" then
  83.           value = tonumber(data:sub(2,-1), 16)
  84.           if value ~= nil then basicforeground = value end
  85.         -- читаем цвет фона для ссылок
  86.         elseif key == "linksback" then
  87.           value = tonumber(data:sub(2,-1), 16)
  88.           if value ~= nil then linksbackground = value end
  89.         else
  90.           -- читаем название странички внутри двойных кавычек
  91.           value = data:match('"(.+)"')
  92.           if key ~= nil and value ~= nil then
  93.             table.insert(pages, {file = key, name = value})
  94.           end
  95.         end
  96.       end
  97.     end
  98.     file:close()
  99.   else
  100.     -- файл конфигурации не найден
  101.     error("No configuration file found.")
  102.   end
  103. end
  104.  
  105. -- загружаем текст странички
  106. function loadPage(filename)
  107.   file = io.open(filename, "r")
  108.   if file ~= nil then
  109.     -- бъем на токены
  110.     explode(file)
  111.     file:close()
  112.     -- рендерим
  113.     renderPage()
  114.     return true
  115.   else
  116.     -- файл странички не найден
  117.     if filename ~= "404.txt"  then
  118.       loadPage("404.txt")
  119.     else
  120.       return false
  121.     end
  122.   end
  123. end
  124.  
  125. -- сканируем файл посимвольно, бьем на токены
  126. local tokens = {}
  127. function explode(file)
  128.   tokens = {}
  129.   word = ""
  130.   while true do
  131.     char = file:read(1)
  132.     -- если достигнут конец файла - прерываем цикл
  133.     if char == nil then
  134.       if word ~= "" then table.insert(tokens, word) end
  135.       break
  136.     -- если перенос
  137.     elseif char == '\n' then
  138.       if word ~= "" then table.insert(tokens, word) end
  139.       table.insert(tokens, "\n")
  140.       word = ""
  141.     -- если открывающая скобка тега
  142.     elseif char == '[' then
  143.       table.insert(tokens, word)
  144.       word = char
  145.     -- закрывающая скобка тега
  146.     elseif char == ']' then
  147.       table.insert(tokens, word..char)
  148.       word = ""
  149.     else
  150.       word = word..char
  151.     end
  152.   end
  153. end
  154.  
  155. -- поиск последнего тега
  156. function getLastTag(tags, name)
  157.   for c=#tags, 1, -1 do
  158.     if tags[c].name == name then
  159.       return tags[c]
  160.     end
  161.   end
  162.   return {name = nil, value = nil}
  163. end
  164.  
  165. -- разбиение строки для переноса по словам
  166. function splitByWords(str, first_length, max_line_length)
  167.   local lines = {}
  168.   local line = ""
  169.   local first = true
  170.   local length = first_length
  171.   str:gsub('(%s*)(%S+)',
  172.     function(spc, word)
  173.       if unicode.len(line) + unicode.len(spc)
  174.                            + unicode.len(word) > length then
  175.         table.insert(lines, line)
  176.         line = word
  177.         if first then
  178.           first = false
  179.           length = max_line_length
  180.         end
  181.       else
  182.         line = line..spc..word
  183.       end
  184.     end
  185.   )
  186.   lspc = str:match("(%s*)$")
  187.   if lspc ~= nil then line = line..lspc end
  188.   table.insert(lines, line)
  189.   return lines
  190. end
  191.  
  192. -- рендерим страничку на основе таблицы токенов
  193. function renderPage()
  194.   local effects = {}
  195.   local line = 1
  196.   local col = 1
  197.  
  198.   render = {{}}
  199.  
  200.   width = screenRes.width - margin*2   -- поля
  201.   for i=1, #tokens do
  202.     -- закрывающий тег
  203.     if tokens[i]:sub(1,2) == '[/' then
  204.       name = tokens[i]:match("%[/(.+)%]")
  205.       -- неуклюже, потом заменю
  206.       if name ~= nil then
  207.         -- закрываем последний тег name
  208.         for c=#effects, 1, -1 do
  209.           if effects[c].name == name then
  210.             table.remove(effects, c)
  211.             break
  212.           end
  213.         end
  214.         -- перенос после центровки
  215.         if name == "center" then
  216.           if tokens[i+1] ~= '\n' then
  217.             col = 1
  218.             line = line + 1
  219.             render[line] = {}
  220.           end
  221.         end
  222.       end
  223.     -- открывающий тег
  224.     elseif tokens[i]:sub(1,1) == '[' then
  225.       name = tokens[i]:match("%[(.+)%]")
  226.       -- цветной текст
  227.       if name:sub(1,5) == "color" then
  228.         hex = name:match("#(......)")
  229.         color = tonumber(hex, 16)
  230.         if color ~= nil then
  231.           table.insert(effects, {name = "color", value = color})
  232.         end
  233.       -- гиперссылки
  234.       elseif name:sub(1,3) == "url" then
  235.         filename = name:match("=(.+)")
  236.         if filename ~= nil then
  237.           table.insert(effects, {name = "url", value = filename})
  238.         end
  239.       -- центрирование
  240.       elseif name == "center" then
  241.         table.insert(effects, {name = "center"})
  242.         -- если центровка начинается не с пустой строки, то перенос
  243.         if col ~= 1 then
  244.           col = 1
  245.           line = line + 1
  246.           render[line] = {}
  247.         end
  248.       -- горизонтальная линия
  249.       elseif name == "---" then
  250.         table.insert(render[line],
  251.           {text = string.rep("-", screenRes.width-margin*2-col+1),
  252.            center = false,
  253.            color = basicforeground,
  254.            x = col})
  255.         col = screenRes.width-margin*2
  256.       end
  257.     -- перенос
  258.     elseif tokens[i] == '\n' then
  259.       col = 1
  260.       line = line + 1
  261.       render[line] = {}
  262.     -- простой текст
  263.     else
  264.       block = {}
  265.       block.color = getLastTag(effects, "color").value
  266.       if block.color == nil then block.color = basicforeground end
  267.       block.url = getLastTag(effects, "url").value
  268.       block.center = (getLastTag(effects, "center").name ~= nil)
  269.      
  270.       -- рубим переносы по словам
  271.       for _, word in ipairs(splitByWords(tokens[i], width-col+1, width)) do
  272.         if col + unicode.len(word) > width then
  273.           col = 1
  274.           line = line + 1
  275.           render[line] = {}
  276.         end
  277.        
  278.         block.x = col
  279.         block.text = word
  280.         table.insert(render[line], {text = block.text, x = block.x,
  281.           color = block.color, url = block.url, center = block.center})
  282.         col = col + unicode.len(word)
  283.       end
  284.     end
  285.   end
  286. end
  287.  
  288. -- вычисляем длину строки рендера
  289. function getLineLen(num)
  290.   local len = 0
  291.   if render[num] ~= nil then
  292.     for _, block in ipairs(render[num]) do
  293.       len = len + unicode.len(block.text)
  294.     end
  295.   end
  296.   return len
  297. end
  298.  
  299.  
  300. -- ============================= B U T T O N S ============================= --
  301. local Button = {}
  302. Button.__index = Button
  303. function Button.new(page, x, y, text, fore, back, width)
  304.   self = setmetatable({}, Button)
  305.  
  306.   self.form = '[ '
  307.   if width == nil then width = 0
  308.     else width = (width - unicode.len(text))-4 end
  309.   for i=1, math.floor(width/2) do
  310.     self.form = self.form.. ' '
  311.   end
  312.   self.form = self.form..text
  313.   for i=1, math.ceil(width/2) do
  314.     self.form = self.form.. ' '
  315.   end
  316.   self.form = self.form..' ]'
  317.  
  318.   self.page = page
  319.  
  320.   self.x = x; self.y = y
  321.   self.fore = fore
  322.   self.back = back
  323.   self.visible = true
  324.  
  325.   return self
  326. end
  327. function Button:draw(fore, back)
  328.   if self.visible then
  329.     local fore = fore or self.fore
  330.     local back = back or self.back
  331.     gpu.setForeground(fore)
  332.     gpu.setBackground(back)
  333.     gpu.set(self.x, self.y, self.form)
  334.   end
  335. end
  336. function Button:click(x, y)
  337.   if self.visible then
  338.     if y == self.y then
  339.       if x >= self.x and x < self.x+unicode.len(self.form) then
  340.         self:draw(self.back, self.fore)
  341.         setPage(self.page, true)
  342.         return true
  343.       end
  344.     end
  345.   end
  346.   return false
  347. end
  348.  
  349. local buttons = {}
  350. function buttonsNew(func, x, y, text, fore, back, width)
  351.   table.insert(buttons, Button.new(func, x, y, text, fore, back, width))
  352. end
  353. function buttonsDraw()
  354.   for i=1, #buttons do
  355.     buttons[i]:draw()
  356.   end
  357. end
  358. function buttonsClick(x, y)
  359.   for i=1, #buttons do
  360.     buttons[i]:click(x, y)
  361.   end
  362. end
  363.  
  364. -- ================================= G U I ================================= --
  365. -- создаем нижнее меню
  366. function generateButtons()
  367.   rows = {0}
  368.   rowlen = 0
  369.   for _, page in ipairs(pages) do
  370.     len = unicode.len(page.name) + 4
  371.     if (rowlen + len) > (screenRes.width - 2) then
  372.       table.insert(rows, 1)
  373.       -- отступ строки кнопок
  374.       rows[#rows-101] = math.floor((screenRes.width-2-rowlen) / 2) - 2
  375.       rowlen = len
  376.     else
  377.       rows[#rows] = rows[#rows] + 1
  378.       rowlen = rowlen + len
  379.     end
  380.   end
  381.   rows[#rows-100] = (screenRes.width-2-rowlen) / 2
  382.  
  383.   i = 1
  384.   for y=1, #rows do
  385.     dx = 2
  386.     for x=1, rows[y] do
  387.       len = unicode.len(pages[i].name)+4
  388.       buttonsNew(pages[i].file, dx+rows[y-100], screenRes.height-#rows+y,
  389.         pages[i].name, basicforeground, linksbackground, len)
  390.       dx = dx + len + 1
  391.       i = i + 1
  392.     end
  393.   end
  394. end
  395.  
  396. -- рисуем панель кнопок
  397. function drawMenu()
  398.   buttonsDraw()
  399. end
  400.  
  401. -- рисуем страницу, начиная с заданной строки
  402. function drawPage(lineNum)
  403.    local height = screenRes.height - #rows   -- нижняя панель
  404.    local y = 1
  405.    -- чистим экран
  406.    gpu.setBackground(basicbackground)
  407.    gpu.setForeground(basicforeground)
  408.    gpu.fill(1,1, screenRes.width, height, ' ')
  409.    -- рисуем
  410.    for i=lineNum, math.min(#render, lineNum+height-1) do
  411.      offset = 0
  412.      for _, block in ipairs(render[i]) do
  413.        if block.center and offset == 0 then
  414.          offset = (screenRes.width - margin*2 - getLineLen(i))/2
  415.        end
  416.        gpu.setForeground(block.color)
  417.        if block.url ~= nil then
  418.          gpu.setBackground(linksbackground)
  419.        else
  420.          gpu.setBackground(basicbackground)
  421.        end
  422.        gpu.set(block.x+margin+offset, y, block.text)
  423.      end
  424.      y = y + 1
  425.    end
  426.    -- полоса прокрутки
  427.    if #render > height then
  428.       gpu.setForeground(basicforeground)
  429.       gpu.setBackground(linksbackground)
  430.       gpu.set(screenRes.width-1, height/2-2, "/\\")
  431.       gpu.set(screenRes.width-1, height/2-1, "/\\")
  432.       gpu.set(screenRes.width-1, height/2,   "/\\")
  433.      
  434.       gpu.set(screenRes.width-1, height/2+5, "\\/")
  435.       gpu.set(screenRes.width-1, height/2+4, "\\/")
  436.       gpu.set(screenRes.width-1, height/2+3, "\\/")
  437.    end
  438. end
  439.  
  440. -- сборная функция для обновления страницы
  441. function setPage(filename, refresh)
  442.   loadPage(filename)
  443.   drawPage(1)
  444.   currentline = 1
  445.   if refresh then drawMenu() end
  446. end
  447.  
  448. -- проверяем, была ли нажата гиперссылка
  449. function linksClick(x, y)
  450.   local dy = currentline + y - 1
  451.   if render[dy] ~= nil then
  452.     for _, block in ipairs(render[dy]) do
  453.       if block.url ~= nil then
  454.         if x >= block.x and x <= block.x + unicode.len(block.text) then
  455.           setPage(block.url)
  456.         end
  457.       end
  458.     end
  459.   end
  460. end
  461.  
  462. -- проверяем, были ли нажаты стрелки слайдера
  463. function sliderClick(x, y)
  464.   local height = screenRes.height - #rows
  465.   if #render > height then
  466.     local half = height/2
  467.     if x > screenRes.width-2 then
  468.       if y >= half-3 and y <= half then
  469.         if currentline > 1 then
  470.           currentline = math.max(1, currentline - height + 1)
  471.           drawPage(currentline)
  472.         end
  473.       elseif y <= half+5 and y >= half+2 then
  474.         if currentline < (#render-height+1) then
  475.           currentline = currentline + height - 1
  476.           drawPage(currentline)
  477.         end
  478.       end
  479.     end
  480.   end
  481. end
  482.  
  483. -- "костыль" для обновления экрана
  484. function updateScreen()
  485.   gpu.setBackground(basicbackground)
  486.   gpu.set(1,1, '_')
  487.   gpu.set(1,1, ' ')
  488. end
  489.  
  490.  
  491. -- ================================ I N I T ================================ --
  492. -- конфиги
  493. loadConfig("info.cfg")
  494. -- определяем размер доступного монитора
  495. screenSize.width, screenSize.height = com.screen.getAspectRatio()
  496. -- вычисляем размеры текстового экрана, исходя из размера монитора
  497. screenRes.width = math.ceil(screenSize.width * textRatio) + 2
  498. screenRes.height = math.ceil(screenSize.height * textRatio / 2)
  499. -- изменяем
  500. oldRes.width, oldRes.height = gpu.getResolution()
  501. gpu.setResolution(screenRes.width, screenRes.height)
  502.  
  503. -- очищаем экран
  504. gpu.setForeground(basicforeground)
  505. gpu.setBackground(basicbackground)
  506. term.clear()
  507.  
  508. -- создаем кнопки
  509. generateButtons()
  510.  
  511. -- загружаем стартовую страницу и рисуем все
  512. setPage("index.txt", true)
  513.  
  514. -- устанавливаем время последнего обновления
  515. lasttime = computer.uptime()
  516.  
  517. -- ========================== M A I N   C Y C L E ========================== --
  518. -- главный цикл работает до нажатия кнопки
  519. -- поскольку клавиатура есть только у админа
  520. while true do
  521.   local name, add, x, y = event.pull(3)
  522.   if name == 'touch' or name == 'drag' then
  523.     buttonsClick(x,y)
  524.     linksClick(x,y)
  525.     sliderClick(x,y)
  526.     lasttime = computer.uptime()
  527.   elseif name == nil then
  528.   elseif name == "key_down" then break end
  529.  
  530.   -- в случае бездействия в DOWNTIME секунд - сброс на главную
  531.   if computer.uptime() - lasttime > downtime then
  532.     setPage("index.txt", true)
  533.     lasttime = computer.uptime()
  534.   end
  535.  
  536.   -- костыль, чтобы экран не гас после рестартов
  537.   updateScreen()
  538. end
  539.  
  540. -- возвращаем экран в прежний вид
  541. gpu.setForeground(0xFFFFFF)
  542. gpu.setBackground(0x000000)
  543. gpu.setResolution(oldRes.width, oldRes.height)
  544. term.clear()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top