Advertisement
MoonlightOwl

InfoPanel IT 1.7.10

Jan 17th, 2015
758
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.75 KB | None | 0 0
  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()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement