Advertisement
Encreedem

Graffiti v1.2

Jul 16th, 2013
167
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 44.96 KB | None | 0 0
  1. -- v1.2
  2.  
  3. -- fields for users
  4. local userFunctions = {}
  5. local userLists = {}
  6. local selectedItems = {}
  7. local userInputs = {}
  8.  
  9. --monitor
  10. local monitorSide = "right"
  11. local monitor = nil
  12.  
  13. -- texts
  14. local refreshText = "Refresh"
  15. local backText = "Back"
  16. local doneString = "Done"
  17.  
  18. -- colors
  19. local buttonDefaultColor = colors.red
  20. local buttonPressedColor = colors.lime
  21. local sliderLowValueColor = colors.red
  22. local sliderMediumValueColor = colors.yellow
  23. local sliderHighValueColor = colors.lime
  24. local inputDefaultColor = colors.white
  25. local inputPressedColor = colors.yellow
  26. local listDefaultColor = colors.lightBlue
  27. local listSelectedColor = colors.orange
  28. local editorMoveColor = colors.magenta
  29. local editorScaleColor = colors.pink
  30.  
  31. -- sizes
  32. local buttonDefaultWidth = 10
  33. local buttonDefaultHeight = 3
  34. local sliderDefaultLength = 10
  35.  
  36. -- editor options
  37. local editMode = false
  38. local showEditorOptions = false
  39. local editActions = { "Design", "Attributes", "Delete" }
  40. local lastScreen = "mainScreen"
  41.  
  42. -- AddOn options
  43. local addOns = {}
  44. local addOnExtension = ".add"
  45.  
  46. -- other
  47. local args = { ... }
  48. local quit = false
  49. local maxX, maxY = 51, 19
  50. local autoLoadObjects = true
  51. local changeButtonColor = true
  52. local screens = {}
  53. screens.mainScreen = {}
  54. local currentScreen = "mainScreen"
  55. local objectTypes = { "Button", "Text", "Variable", "Slider", "Input", "List" }
  56. local editorFunctions = {}
  57.  
  58. -- Displays a star in the upper left corner for a
  59. -- short amount of time. Used when you want to see
  60. -- when something certain happens.
  61. -- Should only be used when you are desperately
  62. -- looking for a bug.
  63. function extremeDebug()
  64.   term.setCursorPos(1, 1)
  65.   term.write("*")
  66.   os.sleep(0.5)
  67.   term.setCursorPos(1, 1)
  68.   term.write(" ")
  69.   os.sleep(0.5)
  70. end
  71.  
  72. -- user variables
  73. local randomValue= 50
  74.  
  75. -- user functions
  76.  
  77. function userFunctions.setRandomValue()
  78.   randomValue = math.random(100)
  79. end
  80.  
  81. -- user lists
  82.  
  83. userLists.testList = {
  84.   "Testitem 1",
  85.   "Testitem 2",
  86.   "Testitem 3"
  87. }
  88.  
  89. -- Define the value of a variable-object.
  90. function getVariableValue(variable)
  91.   if (variable == nil or variable.objType ~= "Variable") then
  92.     return
  93.   end
  94.  
  95.   variableID = variable.varID
  96.   if (variableID == "testVariable") then
  97.     return "Variable";
  98.   elseif (variableID == "Time") then
  99.     return textutils.formatTime(os.time(), true)
  100.   end
  101.  
  102.   return ""
  103. end
  104.  
  105. -- Definie the value of a slider-object
  106. -- 0: empty; 100: full
  107. function getSliderValue(slider)
  108.   if (slider == nil or slider.objType ~= "Slider") then
  109.     return
  110.   end
  111.  
  112.   sliderID = slider.sliderID
  113.  
  114.   if (sliderID == "testSlider") then
  115.     return 87;
  116.   elseif (sliderID == "randomSlider") then
  117.     return randomValue
  118.   end
  119. end
  120.  
  121. -- WARNING! Everything below this comment
  122. -- shouldn't be edited! If you do so and the program
  123. -- doesn't work any more then it's your fault!
  124.  
  125. function readUserInput(message, isPassword)
  126.   term.restore()
  127.   print(message)
  128.  
  129.   if isPassword  then
  130.     ret = read("*")
  131.   else
  132.     ret = read()
  133.   end
  134.  
  135.   term.redirect(monitor)
  136.   return ret
  137. end
  138.  
  139. -- Redirects the input to the computer and lets
  140. -- the user enter something. The result will be
  141. -- in the userInputs array with the inputID as the
  142. -- key.
  143. function getUserInput(inputObject)
  144.   if (inputObject == nil or inputObject.objType ~= "Input") then
  145.     return
  146.   end
  147.  
  148.   x = inputObject.x
  149.   y = inputObject.y
  150.   inputID = inputObject.inputID
  151.   message = inputObject.message
  152.   isPassword = (inputObject.isPassword == nil) and false or inputObject.isPassword
  153.   maxLength = inputObject.maxLength
  154.  
  155.   existingInput = userInputs[inputID]
  156.   if (existingInput ~= nil) then -- Clear the text on the input object.
  157.     term.setCursorPos(x, y)
  158.     for i = -1, string.len(existingInput) do
  159.       term.write(" ")
  160.     end
  161.    
  162.     userInputs[inputID] = nil
  163.   end
  164.  
  165.   -- make the input-object yellow
  166.   term.setCursorPos(x, y)
  167.   term.setBackgroundColor(colors.yellow)
  168.   term.write("  ")
  169.   term.setBackgroundColor(colors.black)
  170.  
  171.   userInput = readUserInput(message, isPassword)
  172.   if (userInput ~= nil) then
  173.     userInputs[inputID] = userInput
  174.   end
  175.  
  176.   term.setCursorPos(x, y)
  177.   term.setBackgroundColor(colors.white)
  178.   term.setTextColor(colors.black)
  179.  
  180.   term.write(" ")
  181.   if (userInput ~= nil and userInput ~= "") then
  182.     if isPassword then
  183.       for i = 1, string.len(userInput) do
  184.         term.write("*")
  185.       end
  186.     else
  187.       term.write(userInput)
  188.     end
  189.   end
  190.  
  191.   term.write(" ")
  192.   term.setBackgroundColor(colors.black)
  193.   term.setTextColor(colors.white)
  194.  
  195.   return ret
  196. end
  197.  
  198. -- Checks if dir is a valid direction-string
  199. function isValidDirection(dir)
  200.   if (dir == "left" or
  201.       dir == "up" or
  202.       dir == "right" or
  203.       dir == "down") then
  204.     return true
  205.   end
  206.  
  207.   return false
  208. end
  209.  
  210. -- display objects region start --
  211.  
  212. function showBox(x, y, width, height, color)
  213.   for row = x, x + width - 1 do
  214.     for col = y, y + height - 1 do
  215.       paintutils.drawPixel(row, col, color)
  216.     end
  217.   end
  218.  
  219.   term.setBackgroundColor(colors.black)
  220. end
  221.  
  222. -- Displays the text on the screen.
  223. function showText(textObject)
  224.   if (textObject.objType ~= "Text") then
  225.     return
  226.   end
  227.  
  228.   x = textObject.x
  229.   y = textObject.y
  230.   text = textObject.text
  231.   assert(x, "Text: X-coordinate has to be set!")
  232.   assert(y, "Text: Y-coordinate has to be set!")
  233.  
  234.   term.setCursorPos(x, y)
  235.   term.write(text)
  236. end
  237.  
  238. -- Displays the slider on the screen.
  239. function showSlider(slider, fillPercentage)
  240.   if (slider == nil or slider.objType ~= "Slider") then
  241.     return
  242.   end
  243.  
  244.   x = slider.x
  245.   y = slider.y
  246.   length = slider.length
  247.   direction = (isValidDirection(slider.direction)) and slider.direction or "right"
  248.  
  249.   startSymbol, endSymbol = "<", ">"
  250.   addX, addY = 1, 0 -- Sets the direction of the slider, therefore it could even be diagonal.
  251.  
  252.   if (direction == "left") then
  253.     addX, addY = -1, 0
  254.     startSymbol, endSymbol = ">", "<"
  255.     term.setCursorPos(x - length, y)
  256.     term.write(endSymbol)
  257.   elseif (direction == "up") then
  258.     addX, addY = 0, -1
  259.     startSymbol, endSymbol = "V", "^"
  260.     term.setCursorPos(x, y - length)
  261.     term.write(endSymbol)
  262.   elseif (direction == "right") then
  263.     addX, addY = 1, 0
  264.     startSymbol, endSymbol = "<", ">"
  265.     term.setCursorPos(x + length, y)
  266.     term.write(endSymbol)
  267.   elseif (direction == "down") then
  268.     addX, addY = 0, 1
  269.     startSymbol, endSymbol = "^", "V"
  270.     term.setCursorPos(x, y + length)
  271.     term.write(endSymbol)
  272.   else -- return if it's not a valid direction, even if I checked it before
  273.     return
  274.   end
  275.  
  276.   term.setCursorPos(x, y)
  277.   term.write(startSymbol)
  278.  
  279.   if (fillPercentage ~= nil) then
  280.     if (fillPercentage < 33) then
  281.       sliderColor = sliderLowValueColor
  282.     elseif (fillPercentage > 66) then
  283.       sliderColor = sliderHighValueColor
  284.     else
  285.       sliderColor = sliderMediumValueColor
  286.     end
  287.    
  288.     filled = math.floor((length / 100) * fillPercentage)
  289.     currentX = x + addX
  290.     currentY = y + addY
  291.    
  292.     for i = 1, filled do
  293.       paintutils.drawPixel(currentX, currentY, sliderColor)
  294.       currentX = currentX + addX
  295.       currentY = currentY + addY
  296.     end
  297.   end
  298.  
  299.   term.setBackgroundColor(colors.black)
  300. end
  301.  
  302. -- Displays the given button on the screen.
  303. function showButton(button, color)
  304.   if (button == nil or button.objType ~= "Button") then
  305.     return
  306.   end
  307.  
  308.   x = button.x
  309.   y = button.y
  310.   width = button.width
  311.   height = button.height
  312.   text = button.text
  313.  
  314.   showBox(x, y, width, height, color)
  315.  
  316.   -- Tries to center the text in the button.
  317.   textCol = x + math.floor((width - string.len(text)) / 2)
  318.   textRow = y + math.ceil(height / 2) - 1
  319.   term.setCursorPos(textCol, textRow)
  320.   term.setBackgroundColor(color)
  321.   term.write(text)
  322.  
  323.   term.setBackgroundColor(colors.black)
  324. end
  325.  
  326. -- Displays the input-object (two white spaces)
  327. function showInput(inputObject)
  328.   if (inputObject == nil or inputObject.objType ~= "Input") then
  329.     return
  330.   end
  331.  
  332.   inputId = inputObject.inputID
  333.   x = inputObject.x
  334.   y = inputObject.y
  335.  
  336.   term.setCursorPos(x, y)
  337.   term.setBackgroundColor(inputDefaultColor)
  338.   term.write(" ")
  339.   if (userInputs[inputID] ~= nil) then
  340.     term.write(userInputs[inputID])
  341.   end
  342.   term.write(" ")
  343.  
  344.   term.setBackgroundColor(colors.black)
  345. end
  346.  
  347. -- Used by "showList" and "showSelector" to
  348. -- determine how wide the list should be.
  349. function getLongestString(stringArray)
  350.   if (stringArray == nil or #stringArray == 0) then
  351.     return 0
  352.   end
  353.  
  354.   ret = 0
  355.  
  356.   for key, value in pairs(stringArray) do
  357.     length = string.len(value)
  358.     if (length > ret) then
  359.       ret = length
  360.     end
  361.   end
  362.  
  363.   return ret
  364. end
  365.  
  366. -- Displays a list on the monitor.
  367. function showList(listObject)
  368.   if (listObject == nil or listObject.objType ~= "List") then
  369.     return
  370.   end
  371.  
  372.   if (type(listObject.elements) == "string") then
  373.     listObject.elements = { listObject.elements }
  374.   end
  375.  
  376.   if (#listObject.elements == 1 and userLists[listObject.elements[1]] ~= nil) then
  377.     listObject.elements = userLists[listObject.elements[1]]
  378.   end
  379.  
  380.   x = listObject.x
  381.   y = listObject.y
  382.   elements = (listObject.elements ~= nil) and listObject.elements or { "empty" }
  383.   width = getLongestString(elements) + 2
  384.   listObject.width = width
  385.   height = #elements
  386.   listObject.height = height
  387.   elements = listObject.elements
  388.   listID = listObject.listID
  389.   isMultiselect = (listObject.isMultiselect ~= nil) and listObject.isMultiselect or false
  390.  
  391.   showBox(x, y, width, height, listDefaultColor)
  392.  
  393.   if (selectedItems[listID] == nil and isMultiselect) then
  394.     selectedItems[listID] = {  }
  395.     for index, elementKey in ipairs(elements) do
  396.       selectedItems[listID][elementKey] = false
  397.     end
  398.   end
  399.  
  400.   posY = 0
  401.   for key,element in pairs(elements) do
  402.     term.setCursorPos(x, y + posY)
  403.    
  404.     if (isMultiselect) then
  405.       if (selectedItems[listID][elements] == true) then
  406.         term.setBackgroundColor(listSelectedColor)
  407.       else
  408.         term.setBackgroundColor(listDefaultColor)
  409.       end
  410.     else
  411.       if (selectedItems[listID] == key) then
  412.         term.setBackgroundColor(listSelectedColor)
  413.       else
  414.         term.setBackgroundColor(listDefaultColor)
  415.       end
  416.     end
  417.    
  418.     term.write(" " .. element .. " ")
  419.     posY = posY + 1
  420.   end
  421.  
  422.   term.setBackgroundColor(colors.black)
  423. end
  424.  
  425. -- Displays a list and returns the field that the
  426. -- user touched.
  427. function showSelector(x, y, elements)
  428.   width = getLongestString(elements) + 2
  429.   height = #elements + 2 -- Elements + up and down
  430.   elementCount = #elements
  431.   displayCount = elementCount
  432.  
  433.   enoughXSpace = true
  434.   -- determine where the selector should actually be displayed
  435.   if (width > maxX) then -- Not enough monitors horizontally?
  436.     x = 1
  437.     enoughXSpace = false
  438.   elseif (maxX - x < width) then -- Not enough space to the right.
  439.     if (x >= width) then -- Let's see if there is space to the left.
  440.       x = x - width
  441.     else -- No space? Check where you've got more space.
  442.       if (maxX / 2) > x then -- More space to the left.
  443.         x = maxX - width + 1
  444.         enoughXSpace = false
  445.       else -- More space to the right
  446.         x = 1
  447.         enoughXSpace = false
  448.       end
  449.     end
  450.   else -- Enough space to the right.
  451.     x = x + 1
  452.   end
  453.  
  454.   if (height > maxY - y) then -- Not enough space from y to bottom.
  455.     if ((maxY / 2) > y) then -- More space below y.
  456.       if enoughXSpace then
  457.         if (maxY < height) then -- Too big for the whole screen.
  458.           y = 1
  459.           displayCount = maxY - 2
  460.         else -- Enough space next to x and not too high.
  461.           y = maxY - height
  462.         end
  463.       else -- Can't display it next to the selected point.
  464.         y = y + 1
  465.         displayCount = maxY - y - 1
  466.       end
  467.     else -- More space above y.
  468.       if enoughXSpace then
  469.         if (y < height) then -- Not enough space from top to y.
  470.           if (maxY < height) then -- Too big for the whole screen.
  471.             y = 1
  472.             displayCount = maxY - 2
  473.           else -- Enough space next to x and not too high.
  474.             y = 1
  475.           end
  476.         else -- Enough space from top to y.
  477.           y = y - height + 1
  478.         end
  479.       else
  480.         if (y < height) then -- Not enough space from top to y.
  481.           if (maxY < height) then -- Too big for the whole screen.
  482.             y = 1
  483.             displayCount = maxY - 2
  484.           else -- Not enough space next to x but not too high.
  485.             y = 1
  486.             displayCount = height - 4
  487.           end
  488.         else -- Enough space from top to y.
  489.           y = y - height
  490.         end
  491.       end
  492.     end
  493.   end
  494.  
  495.   term.setBackgroundColor(colors.black)
  496.  
  497.   -- Read the user input.
  498.   scroll = 1
  499.   right = x + width - 1
  500.   bottom = y + displayCount + 1
  501.  
  502.   finished = false
  503.   while not finished do
  504.     -- Display the actual selector.
  505.     showBox(x, y, width, height, listDefaultColor)
  506.    
  507.     term.setBackgroundColor(listDefaultColor)
  508.     middle = math.floor(width / 2)
  509.     term.setCursorPos(x + middle, y)
  510.     term.write("^")
  511.     term.setCursorPos(x + middle, bottom)
  512.     term.write("V")
  513.    
  514.     for i = 1, displayCount do
  515.       term.setCursorPos(x, y + i)
  516.       term.write(" " .. elements[i + scroll - 1] .. " ")
  517.     end
  518.     term.setBackgroundColor(colors.black)
  519.    
  520.     key, side, touchX, touchY = os.pullEvent("monitor_touch")
  521.    
  522.     if (touchX < x or touchX > right or touchY < y or touchY > bottom) then
  523.       selectedItem = nil
  524.       result = false
  525.       finished = true
  526.     else -- User touched the selector.
  527.       if (touchY == y) then -- up
  528.         if (scroll > 1) then -- Check if it makes sense to scroll up.
  529.           scroll = scroll - 1
  530.         end
  531.       elseif (touchY == bottom) then -- down
  532.         if (displayCount < elementCount) then
  533.           if (scroll <= elementCount - displayCount) then
  534.             scroll = scroll + 1
  535.           end
  536.         end
  537.       else
  538.         selectedItem = elements[touchY - y + scroll - 1]
  539.         result = true
  540.         finished = true
  541.       end
  542.     end
  543.   end
  544.  
  545.   showScreen(currentScreen)
  546.   return result
  547. end
  548.  
  549. -- Displays the text with red background colour.
  550. function showSimpleButton(x, y, text)
  551.   term.setCursorPos(x, y)
  552.   term.setBackgroundColor(colors.red)
  553.   term.write(text)
  554.   term.setBackgroundColor(colors.black)
  555. end
  556.  
  557. -- Displays the "Back"- and "Refresh"-Buttons
  558. function showDefaultButtons()
  559.   x = maxX - string.len(refreshText) + 1
  560.   showSimpleButton(x, maxY, refreshText)
  561.  
  562.   showSimpleButton(1, maxY, backText)
  563. end
  564.  
  565. -- display objects region end
  566.  
  567. function getSystemInfo()
  568.   systemInfo = {}
  569.   systemInfo.maxX = maxX
  570.   systemInfo.maxY = maxY
  571.   systemInfo.selectedItems = selectedItems
  572.   systemInfo.userInputs = userInputs
  573.  
  574.   return systemInfo
  575. end
  576.  
  577. -- Loads the values of all variables and sliders
  578. -- of the current screen.
  579. function loadObjects()
  580.   for objectID, object in pairs(screens[currentScreen]) do
  581.     objectType = object.objType
  582.     x = object.x
  583.     y = object.y
  584.    
  585.     if (objectType == "Variable") then
  586.       value = getVariableValue(object)
  587.       term.setCursorPos(x, y)
  588.       term.write(value)
  589.     elseif (objectType == "Slider") then
  590.       length = object.length
  591.       value = getSliderValue(object)
  592.       showSlider(object, value)
  593.     end
  594.   end
  595. end
  596.  
  597. -- Displays all objects of the selected screen.
  598. function showScreen(screenID)
  599.   term.clear()
  600.  
  601.   currentScreen = screenID
  602.  
  603.   if not editMode then
  604.     if (currentScreen == "mainScreen") then
  605.       backText = "Quit"
  606.     else
  607.       backText = "Back"
  608.     end
  609.   else
  610.     backText = "Quit"
  611.     refreshText = "Options"
  612.   end
  613.  
  614.   local screenObject
  615.   local objectType
  616.   if showEditorOptions then
  617.     screenObject = editorScreens[screenID]
  618.   else
  619.     screenObject = screens[screenID]
  620.   end
  621.  
  622.   for sObjectID, sObject in pairs(screenObject) do
  623.     objectType = sObject.objType
  624.    
  625.     if (objectType == "Button") then
  626.       showButton(sObject, buttonDefaultColor)
  627.     elseif (objectType == "Text") then
  628.       showText(sObject)
  629.     elseif (objectType == "Slider") then
  630.       showSlider(sObject, 0)
  631.     elseif (objectType == "Input") then
  632.       showInput(sObject)
  633.     elseif (objectType == "List") then
  634.       showList(sObject)
  635.     elseif (objectType == "Custom") then
  636.       callAddOn(sObject, "Show")
  637.     end
  638.   end
  639.  
  640.   if autoLoadObjects then
  641.     loadObjects()
  642.   end
  643.  
  644.   showDefaultButtons()
  645.  
  646.   term.setCursorPos(1, maxY)
  647. end
  648.  
  649. -- Waits until the user touches the monitor and
  650. -- if he touched a button, the function stored in
  651. -- it will be called.
  652. function getInput()
  653.   finished = false
  654.   key, side, x, y = os.pullEvent("monitor_touch")
  655.  
  656.   if (y == maxY) then -- Checking the default buttons
  657.     if (x <= string.len(backText)) then -- "Back"-Button pressed
  658.       if (currentScreen == "mainScreen" or editMode) then
  659.         quit = true
  660.       else
  661.         if (screens[currentScreen].parentScreen ~= nil) then
  662.           showScreen(screens[currentScreen].parentScreen)
  663.           finished = true
  664.         else
  665.           showScreen("mainScreen")
  666.           finished = true
  667.         end
  668.       end
  669.     elseif (x >= maxX - string.len(refreshText) and not editMode) then -- "Refresh"-Button pressed
  670.       showScreen(currentScreen)
  671.       finished = true
  672.     end
  673.   end
  674.  
  675.   if (finished == true or quit==true) then
  676.     return nil
  677.   end
  678.  
  679.   if editMode then
  680.     screenObject = editorScreens[currentScreen]
  681.   else
  682.     screenObject = screens[currentScreen]
  683.   end
  684.  
  685.   for sObjectID, sObject in pairs(screenObject) do
  686.     objectType = sObject.objType
  687.    
  688.     if (objectType == "Button") then
  689.       left = sObject.x
  690.       top = sObject.y
  691.       width = sObject.width
  692.       height = sObject.height
  693.       right = left + width
  694.       bottom = top + height
  695.      
  696.       if (x >= left and x < right and y >= top and y < bottom) then
  697.         if ((sObject.funcType ~= nil and sObject.param ~= nil) or sObject.isAddOn) then
  698.           if (sObject.isAddOn == true) then
  699.             callAddOn(sObject, "Click")
  700.           else
  701.             callAction(sObject.funcType, sObject.param, sObject)
  702.           end
  703.          
  704.           finished = true
  705.         end
  706.       end
  707.     elseif (objectType == "Input") then
  708.       left = sObject.x
  709.       top = sObject.y
  710.       inputID = sObject.inputID
  711.       message = sObject.message
  712.       isPassword = sObject.isPassword
  713.      
  714.       if ((x == left or x == left + 1) and y == top) then
  715.         getUserInput(sObject)
  716.       end
  717.     elseif (objectType == "List") then
  718.       left = sObject.x
  719.       top = sObject.y
  720.       width = sObject.width
  721.       height = #sObject.elements
  722.       right = left + width
  723.       bottom = top + height
  724.      
  725.       listID = sObject.listID
  726.       isMultiselect = sObject.isMultiselect
  727.      
  728.       if (x >= left and x < right and y >= top and y < bottom) then
  729.         if (isMultiselect) then
  730.           if (selectedItems[listID][y - top + 1]) then
  731.             selectedItems[listID][y - top + 1] = false
  732.           else
  733.             selectedItems[listID][y - top + 1] = true
  734.           end
  735.         else
  736.           selectedItems[listID] = y - top + 1
  737.         end
  738.        
  739.         showList(sObject)
  740.        
  741.         finished = true
  742.       end
  743.     elseif (objectType == "Custom" and sObject.canClick) then -- AddOn Object
  744.       left = sObject.x
  745.       top = sObject.y
  746.       width = sObject.width
  747.       height = sObject.height
  748.       right = left + width
  749.       bottom = top + height
  750.      
  751.       if (x >= left and x < right and y >= top and y < bottom) then
  752.         callAddOn(sObject, "Click")
  753.       end
  754.     end
  755.    
  756.     if (finished == true) then
  757.       break
  758.     end
  759.   end
  760. end
  761.  
  762. function callAction(actionType, param, button)
  763.   if (actionType == "switch") then
  764.     showScreen(param)
  765.   elseif (actionType == "function") then
  766.     if changeButtonColor then
  767.       showButton(button, buttonPressedColor)
  768.     end
  769.    
  770.     if userFunctions[param] ~= nil then
  771.       userFunctions[param]()
  772.     elseif editorFunctions[param] ~= nil then
  773.       editorFunctions[param]()
  774.     end
  775.    
  776.     if changeButtonColor then
  777.       showButton(button, buttonDefaultColor)
  778.     else
  779.       changeButtonColor = true
  780.     end
  781.   end
  782. end
  783.  
  784. function callAddOn(object, callType)
  785.   addOnName = object.addOnName
  786.   objectID = object.objID
  787.   x = object.x
  788.   y = object.y
  789.   width = object.width
  790.   height = object.height
  791.   addOnPath = fs.combine(shell.dir(), addOnName .. addOnExtension)
  792.  
  793.   systemInfo = getSystemInfo()
  794.   systemInfo.x = x
  795.   systemInfo.y = y
  796.   systemInfo.width = width
  797.   systemInfo.height = height
  798.  
  799.   shell.run(addOnPath, callType, objectID, textutils.serialize(systemInfo))
  800. end
  801.  
  802. function getMonitor()
  803.   if (peripheral.getType(monitorSide) == "monitor") then
  804.     monitor = peripheral.wrap(monitorSide)
  805.     return true
  806.   else
  807.     return false
  808.   end
  809. end
  810.  
  811. -- Shows the message on the computer for debugging. Probably my most-used function.
  812. function debugMessage(message)
  813.   term.restore()
  814.   print(message)
  815.   term.redirect(monitor)
  816. end
  817.  
  818. function main()
  819.   showScreen("mainScreen")
  820.  
  821.   while not quit do
  822.     getInput()
  823.   end
  824. end
  825.  
  826. function saveScreens()
  827.   saveString = textutils.serialize(screens)
  828.   file = fs.open("screens.sav", "w")
  829.   file.write(saveString)
  830.   file.close()
  831. end
  832.  
  833. function loadScreens()
  834.   if not fs.exists("screens.sav") then
  835.     return
  836.   end
  837.  
  838.   file = fs.open("screens.sav", "r")
  839.   loadString = file.readAll()
  840.   if (loadString ~= nil and loadString ~= "") then
  841.     screens = textutils.unserialize(loadString)
  842.   end
  843.   file.close()
  844. end
  845.  
  846. function splitAt(toSplit, delimiter)
  847.   delimiterPos = string.find(toSplit, delimiter)
  848.   left = string.sub(toSplit, 1, delimiterPos - 1)
  849.   right = string.sub(toSplit, delimiterPos + #delimiter)
  850.  
  851.   return left, right
  852. end
  853.  
  854. -- screen editor region start --
  855.  
  856. function generateScreenList()
  857.   ret = {  }
  858.   for key, value in pairs(screens) do
  859.     table.insert(ret, key)
  860.   end
  861.  
  862.   return ret
  863. end
  864.  
  865. editorScreens = {
  866.   mainScreen = {
  867.     { objType="Text", x=2, y=1, text="Mode:" };
  868.     { objType="List", x=2, y=2, elements=editActions, listID="editActionList", isMultiselect=false };
  869.     { objType="Button", x=2, y=6, width=14, height=1, text="last screen", funcType="function", param="editLastScreen" };
  870.     { objType="Button", x=2, y=8, width=14, height=1, text="edit screens", funcType="function", param="loadScreenList" };
  871.   };
  872.  
  873.   screenListScreen = {
  874.     { objType="List", x=2, y=2, elements=screenList, listID="screenList", isMultiselect=false };
  875.     { objType="Button", x=2, y=maxY-6, width=12, height=1, text="Set parent", funcType="function", param="setParent" };
  876.     { objType="Button", x=2, y=maxY-4, width=8, height=1, text="New", funcType="function", param="newScreen" };
  877.     { objType="Button", x=2, y=maxY-3, width=8, height=1, text="Edit", funcType="function", param="editScreen" };
  878.     { objType="Button", x=2, y=maxY-2, width=8, height=1, text="Delete", funcType="function", param="deleteScreen" };
  879.   };
  880. }
  881.  
  882. -- Used to give a List-object an array of all screens
  883. function editorFunctions.loadScreenList()
  884.   screenList = generateScreenList()
  885.   editorScreens["screenListScreen"][1].elements = screenList
  886.   changeButtonColor = false
  887.   showScreen("screenListScreen")
  888. end
  889.  
  890. function editorFunctions.editLastScreen()
  891.   if (lastScreen == nil) then
  892.     lastScreen = "mainScreen"
  893.   end
  894.  
  895.   showEditorOptions = false
  896.   showScreen(lastScreen)
  897.   changeButtonColor = false
  898. end
  899.  
  900. -- Let's the user define the parentScreen-attribute of the current screen.
  901. function editorFunctions.setParent()
  902.   if (selectedItems.screenList == nil) then
  903.     return
  904.   end
  905.  
  906.   list = editorScreens.screenListScreen[1]
  907.   height = list.height
  908.   for i = 1, height do
  909.     if (selectedItems.screenList ~= i) then
  910.       paintutils.drawPixel(1, i + 1, colors.lime)
  911.     end
  912.   end
  913.  
  914.   event, side, x, y = os.pullEvent("monitor_touch")
  915.  
  916.   if (y > 1 and y <= height + 1) then -- Clicked inside the list.
  917.     if (y - 1 ~= selectedItems.screenList) then -- Selected parentScreen is not selected screen.
  918.       screens[list.elements[selectedItems.screenList]].parentScreen = list.elements[y - 1]
  919.     end
  920.   end
  921.  
  922.   for i = 1, height do
  923.     paintutils.drawPixel(1, i + 1, colors.black)
  924.   end
  925. end
  926.  
  927. -- Creates a new screen. The user has to enter the screen name in the computer.
  928. function editorFunctions.newScreen()
  929.   term.clear()
  930.   term.setCursorPos(2, 2)
  931.   term.write("Enter a screen-name.")
  932.   message = "Pleas enter the name of the new screen."
  933.   userInput = readUserInput(message, false)
  934.  
  935.   while (userInput ~= nil and screens[userInput] ~= nil) do
  936.     message = "There is already a screen with that name!"
  937.     userInput = readUserInput(message, false)
  938.   end
  939.  
  940.   if (userInput ~= nil) then
  941.     screens[userInput] = { parentScreen="mainScreen" }
  942.     showEditorOptions = false
  943.     showScreen(userInput)
  944.     lastScreen = userInput
  945.     changeButtonColor = false
  946.   end
  947. end
  948.  
  949. -- Edits the screen that has been selected in the "screenList"-list.
  950. function editorFunctions.editScreen()
  951.   if (selectedItems.screenList ~= nil) then
  952.     showEditorOptions = false
  953.     lastScreen = screenList[selectedItems.screenList]
  954.     showScreen(screenList[selectedItems.screenList])
  955.     changeButtonColor = false
  956.   end
  957. end
  958.  
  959. -- Deletes the screen that has been selected in the "screenList"-list.
  960. function editorFunctions.deleteScreen()
  961.   if (selectedItems.screenList ~= nil and screenList[selectedItems.screenList] ~= "mainScreen") then
  962.     screens[screenList[selectedItems.screenList]] = nil
  963.     showEditorOptions = true
  964.     editorFunctions.loadScreenList()
  965.   end
  966. end
  967.  
  968. -- Displays an object with default attributes and adds it to the current screen.
  969. function showDefaultObject(objectType, xCoord, yCoord)
  970.   object = {  }
  971.  
  972.   object.objType = objectType
  973.   object.x = xCoord
  974.   object.y = yCoord
  975.  
  976.   maxWidth = maxX - xCoord
  977.   maxHeight = maxY - yCoord
  978.  
  979.   if (string.find(objectType, " - ") ~= nil) then -- object is an AddOn
  980.     addOnName, objectName = splitAt(objectType, " - ")
  981.     objectValues = addOns[addOnName][objectName]
  982.    
  983.     objectID = objectValues.objectID
  984.     objectType = objectValues.objectType
  985.     defaultWidth = tonumber(objectValues.defaultWidth)
  986.     defaultHeight = tonumber(objectValues.defaultHeight)
  987.     canScale = objectValues.canScale
  988.     canClick = objectValues.canClick
  989.    
  990.     object.objID = objectID
  991.     object.objType = objectType
  992.     object.canScale = canScale
  993.     object.canClick = canClick
  994.     object.isAddOn = true
  995.     object.addOnName = addOnName
  996.    
  997.     if (objectType == "Button") then
  998.       object.width = (defaultWidth <= maxWidth) and defaultWidth or maxWidth
  999.       object.height = (defaultHeight <= maxHeight) and defaultHeight or maxHeight
  1000.       object.text = objectValues.text
  1001.       showButton(object, buttonDefaultColor)
  1002.     elseif (objectType == "Custom") then
  1003.       if canScale then
  1004.         object.width = (defaultWidth <= maxWidth) and defaultWidth or maxWidth
  1005.         object.height = (defaultHeight <= maxHeight) and defaultHeight or maxHeight
  1006.       else
  1007.         object.width = defaultWidth
  1008.         object.height = defaultHeight
  1009.       end
  1010.     elseif (objectType == "Variable") then
  1011.       object.varID = objectID
  1012.     elseif (objectType == "Input") then
  1013.       object.inputID = objectID
  1014.       object.message = objectValues.message
  1015.       showInput(object)
  1016.     elseif (objectType == "List") then
  1017.       object.listID = objectID
  1018.       object.elements = objectValues.elements
  1019.       showList(object)
  1020.     end
  1021.   elseif (objectType == "Button") then
  1022.     object.width = (maxWidth < buttonDefaultWidth) and maxWidth or buttonDefaultWidth
  1023.     object.height = (maxHeight < buttonDefaultHeight) and maxHeight or buttonDefaultHeight
  1024.    
  1025.     object.text = "Button"
  1026.     object.funcType = ""
  1027.     object.param = ""
  1028.     showButton(object, buttonDefaultColor)
  1029.   elseif (objectType == "Text") then
  1030.     object.text = "Text"
  1031.     showText(object)
  1032.   elseif (objectType == "Variable") then
  1033.     object.varID = "testVariable"
  1034.   elseif (objectType == "Slider") then
  1035.     object.length = (maxWidth < sliderDefaultLength) and maxWidth or sliderDefaultLength
  1036.     object.direction = "right"
  1037.     object.sliderID = "testSlider"
  1038.     showSlider(object)
  1039.   elseif (objectType == "Input") then
  1040.     object.inputID = "testInput"
  1041.     object.message = "Enter something."
  1042.     object.isPassword = false
  1043.     showInput(object)
  1044.   elseif (objectType == "List") then
  1045.     object.elements = {"List Item 1", "List Item 2", "List Item 3"}
  1046.     object.listID = "testList"
  1047.     object.isMultiselect = false
  1048.     showList(object)
  1049.   else
  1050.     return
  1051.   end
  1052.  
  1053.   table.insert(screens[currentScreen], object)
  1054. end
  1055.  
  1056. -- Returns the right- and bottom-coordinates of the object.
  1057. function getObjectDimensions(object)
  1058.   if (type(object) ~= "table") then
  1059.     return -1, -1, -1, -1
  1060.   end
  1061.  
  1062.   objectType = object.objType
  1063.   left = object.x
  1064.   top = object.y
  1065.  
  1066.   if (objectType == "Button" or objectType == "List") then
  1067.     right = left + object.width - 1
  1068.     bottom = top + object.height - 1
  1069.   elseif (objectType == "Text") then
  1070.     right = left + string.len(object.text) - 1
  1071.     bottom = top
  1072.   elseif (objectType == "Variable" or objectType == "Input") then
  1073.     right = left + 1
  1074.     bottom = top
  1075.   elseif (objectType == "Slider") then
  1076.     direction = object.direction
  1077.     length = object.length
  1078.    
  1079.     if (direction == "left") then
  1080.       left = object.x - length
  1081.       top = object.y
  1082.       right = object.x
  1083.       bottom = top
  1084.     elseif (direction == "up") then
  1085.       left = object.x
  1086.       top = object.y - length
  1087.       right = object.x
  1088.       bottom = object.y
  1089.     elseif (direction == "down") then
  1090.       left = object.x
  1091.       top = object.y
  1092.       right = object.x
  1093.       bottom = top + length
  1094.     else -- right
  1095.       left = object.x
  1096.       top = object.y
  1097.       right = object.x + length
  1098.       bottom = top
  1099.     end
  1100.   elseif (objectType == "Custom") then -- AddOn
  1101.     if (object.canScale or object.canClick) then
  1102.       right = left + object.width
  1103.       bottom = top + object.height
  1104.     else
  1105.       right = left
  1106.       bottom = top
  1107.     end
  1108.   else
  1109.     right = -1
  1110.     bottom = -1
  1111.   end
  1112.  
  1113.   return left, top, right, bottom
  1114. end
  1115.  
  1116. function findObject(x, y)
  1117.   if showEditorOptions then
  1118.     screenObject = editorScreens[currentScreen]
  1119.   else
  1120.     screenObject = screens[currentScreen]
  1121.   end
  1122.   for sObjectID, sObject in pairs(screens[currentScreen]) do
  1123.     left, top, right, bottom = getObjectDimensions(sObject)
  1124.    
  1125.     if (x >= left and x <= right and y >= top and y <= bottom) then
  1126.       return sObjectID, sObject
  1127.     end
  1128.   end
  1129.  
  1130.   return nil, nil
  1131. end
  1132.  
  1133. -- Let's the user delete an object or change its attributes depending on the current edit-mode.
  1134. function editObject(objectKey)
  1135.   sObject = screens[currentScreen][objectKey]
  1136.   objType = sObject.objType
  1137.   left, top, right, bottom = getObjectDimensions(sObject)
  1138.  
  1139.   if (editActions[selectedItems.editActionList] == "Delete") then
  1140.     screens[currentScreen][objectKey] = nil
  1141.   elseif (editActions[selectedItems.editActionList] == "Attributes" and not screens[currentScreen][objectKey].isAddOn) then
  1142.     objAttr = {  }
  1143.    
  1144.     index = 1
  1145.     for key, value in pairs(sObject) do
  1146.       if (key ~= "objType" and key ~= "x" and key ~= "y" and key ~= "width" and key ~= "height" and key ~= "length" and key ~= "direction") then
  1147.         table.insert(objAttr, index, key)
  1148.         index = index + 1
  1149.       end
  1150.     end
  1151.    
  1152.     term.clear()
  1153.    
  1154.     yPos = 2
  1155.     top = yPos
  1156.     for attrKey, attrValue in ipairs(objAttr) do
  1157.       term.setCursorPos(2, yPos)
  1158.       term.write(attrValue .. ": ")
  1159.       print(sObject[attrValue])
  1160.       yPos = yPos + 1
  1161.     end
  1162.     term.setCursorPos(2, yPos + 1)
  1163.     term.setBackgroundColor(colors.red)
  1164.     term.write(doneString)
  1165.     term.setBackgroundColor(colors.black)
  1166.    
  1167.     bottom = yPos - 1
  1168.     finished = false
  1169.     while not finished do
  1170.       event, side, x, y = os.pullEvent("monitor_touch")
  1171.      
  1172.       if y >= top and y <= bottom then
  1173.         selectedAttr = objAttr[y - 1]
  1174.         paintutils.drawPixel(1, y, colors.yellow)
  1175.        
  1176.         if (selectedAttr == "text" or
  1177.             selectedAttr == "param" or
  1178.             selectedAttr == "varID" or
  1179.             selectedAttr == "sliderID" or
  1180.             selectedAttr == "inputID" or
  1181.             selectedAttr == "listID" or
  1182.             selectedAttr == "message" or
  1183.             selectedAttr == "elements" or
  1184.             selectedAttr == "message") then
  1185.           userInput = readUserInput("Please enter a value for the " .. selectedAttr .. ".", false)
  1186.           if (userInput ~= nil) then
  1187.             screens[currentScreen][objectKey][selectedAttr] = userInput
  1188.           end
  1189.         elseif (selectedAttr == "funcType") then -- Button attribute
  1190.           if (sObject.funcType == "switch") then
  1191.             screens[currentScreen][objectKey][selectedAttr] = "function"
  1192.           else
  1193.             screens[currentScreen][objectKey][selectedAttr] = "switch"
  1194.           end
  1195.         elseif (selectedAttr == "isPassword" or selectedAttr == "isMultiselect") then
  1196.           if (sObject[selectedAttr]) then
  1197.             screens[currentScreen][objectKey][selectedAttr] = false
  1198.           else
  1199.             screens[currentScreen][objectKey][selectedAttr] = true
  1200.           end
  1201.         end
  1202.         paintutils.drawPixel(1, y, colors.black)
  1203.         if (not finished and selectedAttr ~= nil) then
  1204.           term.setCursorPos(2, y) -- I don't know if that's neccessary...
  1205.           for i = 2, maxX do
  1206.             term.write(" ")
  1207.           end
  1208.           term.setCursorPos(2, y)
  1209.           term.write(selectedAttr .. ": ")
  1210.           term.write(sObject[selectedAttr])
  1211.         end
  1212.       elseif (y == yPos + 1 and x >= 2 and x <= 1 + string.len(doneString)) then
  1213.         finished = true
  1214.       end
  1215.     end
  1216.   else -- Design mode
  1217.     canScale = false
  1218.     moveX = left
  1219.     moveY = top
  1220.    
  1221.     if (objType == "Button") then
  1222.       paintutils.drawPixel(right, bottom, editorScaleColor)
  1223.       scaleX = right
  1224.       scaleY = bottom
  1225.       canScale = true
  1226.     elseif (objType == "Slider") then
  1227.       direction = sObject.direction
  1228.       assert(direction)
  1229.       canScale = true
  1230.      
  1231.       if (direction == "left") then
  1232.         moveX = right
  1233.         moveY = top
  1234.         scaleX = left
  1235.         scaleY = bottom
  1236.       elseif (direction == "up") then
  1237.         moveX = left
  1238.         moveY = bottom
  1239.         scaleX = right
  1240.         scaleY = top
  1241.       else -- right or down
  1242.         moveX = left
  1243.         moveY = top
  1244.         scaleX = right
  1245.         scaleY = bottom
  1246.       end
  1247.      
  1248.       paintutils.drawPixel(scaleX, scaleY, editorScaleColor)
  1249.     elseif (objType == "Custom" and sObject.canScale) then -- AddOn
  1250.       canScale = true
  1251.       scaleX = right
  1252.       scaleY = bottom
  1253.     end
  1254.    
  1255.     paintutils.drawPixel(moveX, moveY, editorMoveColor)
  1256.     term.setBackgroundColor(colors.black)
  1257.    
  1258.     event, side, x, y = os.pullEvent("monitor_touch")
  1259.    
  1260.     if (x >= left and x <= right and y >= top and y <= bottom) then -- clicked inside the object
  1261.       if (x == moveX and y == moveY) then -- move object
  1262.         paintutils.drawPixel(moveX, moveY, colors.white)
  1263.         event, side, x, y = os.pullEvent("monitor_touch")
  1264.         screens[currentScreen][objectKey].x = x
  1265.         screens[currentScreen][objectKey].y = y
  1266.       elseif (canScale and x == scaleX and y == scaleY) then -- scale object
  1267.         paintutils.drawPixel(scaleX, scaleY, colors.white)
  1268.         term.setBackgroundColor(colors.black)
  1269.         event, side, x, y = os.pullEvent("monitor_touch")
  1270.        
  1271.         if (objType == "Button" or objType == "Custom") then
  1272.           if (x > moveX + 2 and y >= moveY) then
  1273.             screens[currentScreen][objectKey].width = x - left + 1
  1274.             screens[currentScreen][objectKey].height = y - top + 1
  1275.           end
  1276.         elseif (objType == "Slider") then
  1277.           if (x < moveX and y == moveY) then -- Clicked left of the slider.
  1278.             screens[currentScreen][objectKey].direction = "left"
  1279.             screens[currentScreen][objectKey].length = moveX - x
  1280.           elseif (x == moveX and y < moveY) then -- Clicked above the slider.
  1281.             screens[currentScreen][objectKey].direction = "up"
  1282.             screens[currentScreen][objectKey].length = moveY - y
  1283.           elseif (x > moveX and y == moveY) then -- Clicked right of the slider.
  1284.             screens[currentScreen][objectKey].direction = "right"
  1285.             screens[currentScreen][objectKey].length = x - moveX
  1286.           elseif (x == moveX and y > moveY) then -- Clicked below the slider.
  1287.             screens[currentScreen][objectKey].direction = "down"
  1288.             screens[currentScreen][objectKey].length = y - moveY
  1289.           end
  1290.           --screens[currentScreen][objectKey].length = x - left + 1
  1291.         end
  1292.       else -- clicked something else inside the object (no idea what I could use this for)
  1293.        
  1294.       end
  1295.     end
  1296.   end
  1297.  
  1298.   term.setBackgroundColor(colors.black)
  1299.   showScreen(currentScreen)
  1300. end
  1301.  
  1302. function markVariables()
  1303.   for sObjectID, sObject in pairs(screens[currentScreen]) do
  1304.     if (sObject.objType == "Variable") then
  1305.       paintutils.drawPixel(sObject.x, sObject.y, colors.lime)
  1306.       term.setBackgroundColor(colors.black)
  1307.     end
  1308.   end
  1309. end
  1310.  
  1311. function getEditorInput()
  1312.   if not showEditorOptions then
  1313.     markVariables()
  1314.     key, side, xCoord, yCoord = os.pullEvent("monitor_touch")
  1315.   end
  1316.  
  1317.   if (showEditorOptions or yCoord == maxY and xCoord > maxX - string.len(refreshText)) then -- "Refresh" pressed => Options screen
  1318.     showEditorOptions = true
  1319.     showScreen("mainScreen")
  1320.     while showEditorOptions and not quit do
  1321.       getInput()
  1322.     end
  1323.   elseif (yCoord == maxY and xCoord >= 1 and xCoord <= string.len(backText)) then -- "Back" pressed => Quit
  1324.     quit = true
  1325.   else
  1326.     key, value = findObject(xCoord, yCoord) -- Find the object that the user touched.
  1327.     if (key == nil) then -- No object touched. Show selector for new object.
  1328.       paintutils.drawPixel(xCoord, yCoord, colors.white)
  1329.       if (showSelector(xCoord, yCoord, objectTypes)) then -- something has been selected
  1330.         showDefaultObject(selectedItem, xCoord, yCoord)
  1331.       end
  1332.     else
  1333.       editObject(key)
  1334.     end
  1335.   end
  1336. end
  1337.  
  1338. function screenEditor()
  1339.   editMode = true
  1340.   autoLoadObjects = false
  1341.  
  1342.   showEditorOptions = true
  1343.   showScreen("mainScreen")
  1344.  
  1345.   while not quit do
  1346.     getEditorInput()
  1347.   end
  1348. end
  1349.  
  1350. -- screen editor region end --
  1351.  
  1352. -- AddOn manager region start --
  1353.  
  1354. -- Removes all whitespaces to the left and right of the string.
  1355. function trim(s)
  1356.   return s:gsub("^%s*(.-)%s*$", "%1")
  1357. end
  1358.  
  1359. -- Prints the message at the screen and exits the program.
  1360. function throwException(message, fileName, lineNumber)
  1361.   term.restore()
  1362.   error("AddOn loader error when reading file " .. fileName .. " at line " .. lineNumber .. ": " .. message)
  1363. end
  1364.  
  1365. -- Sets the optional attributes to their default-values if they haven't been set.
  1366. function setDefaultAttributes(object)
  1367.   if (object.objectType == "Button") then
  1368.     object.defaultWidth = (object.defaultWidth ~= nil) and object.defaultWidth or buttonDefaultWidth
  1369.     object.defaultHeight = (object.defaultHeight ~= nil) and object.defaultHeight or buttonDefaultHeight
  1370.   elseif (object.objectType == "List") then
  1371.     object.elements = string.gmatch(object.elements, "[^;]+")
  1372.   end
  1373.  
  1374.   object.canScale = (object.canScale ~= nil) and object.canScale or false
  1375.   object.canClick = (object.canClick ~= nil) and object.canClick or false
  1376.  
  1377.   return object
  1378. end
  1379.  
  1380. -- Returns whether the object is valid or not. Returns a message if it isn't.
  1381. function validateAddOnObject(object)
  1382.   if (object == nil or type(object) ~= "table") then
  1383.     return false, "Object is not a table!"
  1384.   end
  1385.  
  1386.   if (object.objectID == nil or object.objectID == "") then
  1387.     return false, "Object has no objecID!"
  1388.   end
  1389.  
  1390.   objType = object.objectType
  1391.   if (objType ~= "Button" and
  1392.       objType ~= "Variable" and
  1393.       objType ~= "Input" and
  1394.       objType ~= "List" and
  1395.       objType ~= "Custom") then
  1396.     return false, "Object has an invalid type!"
  1397.   end
  1398.  
  1399.   if (objType == "Button") then
  1400.     if (object.defaultWidth == nil or
  1401.         object.defaultHeight == nil) then
  1402.       return false, "Button-type objects need to have defaultWidth and defaultHeight attributes!"
  1403.     elseif (object.text == nil) then
  1404.       return false, "Button-type objects need to have a text attribute!"
  1405.     end
  1406.   elseif (objType == "List") then
  1407.     if (object.elements == nil) then
  1408.       return false, "List-type objects need to have an elements-attribute!"
  1409.     end
  1410.   elseif (objType == "Custom") then
  1411.     if (object.canScale or object.canClick) then
  1412.       if (object.defaultWidth == nil or
  1413.         object.defaultHeight == nil) then
  1414.         return false, "Default objects need to have defaultWidth and defaultHeight attributes when canScale or canClick is set to true!"
  1415.       end
  1416.     end
  1417.   end
  1418.  
  1419.   return true, nil
  1420. end
  1421.  
  1422. -- Reads the information between the file's <object> tags and adds the objects to the addOns-table.
  1423. function readAddOn(filePath)
  1424.   file = fs.open(filePath, "r")
  1425.   fileName = string.sub(fs.getName(filePath), 1, -5)
  1426.   addOns[fileName] = {}
  1427.  
  1428.   started = false
  1429.   finished = false
  1430.   lineNumber = 0
  1431.  
  1432.   newObject = nil
  1433.   createNewObject = false
  1434.   newObjectLine = -1
  1435.  
  1436.   while not finished do
  1437.     line = file.readLine()
  1438.     line = trim(line)
  1439.     lineNumber = lineNumber + 1
  1440.    
  1441.     if (line == nil) then -- Reached end of line.
  1442.       throwException("Objects not found!", fileName, lineNumber)
  1443.     elseif (line == "" and started) then
  1444.       throwException("No </objects> tag found in the file! Make sure that you don't have any empty lines between the <objects> tags!", fileName, lineNumber)
  1445.     elseif (line == "<objects>") then
  1446.       if started then
  1447.         throwException("Root tag <objects> can only be used once!", fileName, lineNumber)
  1448.       else
  1449.         started = true
  1450.       end
  1451.     elseif (line == "</objects>") then
  1452.       if createNewObject then -- Code ends without finishing the last object.
  1453.         throwException("No </object> tag found to close <object> at line " .. newObjectLine, fileName, lineNumber)
  1454.       else
  1455.         finished = true
  1456.       end
  1457.     elseif (line == "<object>") then
  1458.       if createNewObject then -- no "</object>" before the next "<object>" tag.
  1459.         throwException("No </object> tag found to close <object> at line " .. newObjectLine, fileName, lineNumber)
  1460.       else
  1461.         createNewObject = true
  1462.         newObject = {}
  1463.         newObjectLine = lineNumber
  1464.       end
  1465.     elseif (line == "</object>") then -- Create object and add it to the addOns-table.
  1466.       if not createNewObject then
  1467.         throwException("No <object> tag found that needs to be closed.", fileName, lineNumber)
  1468.       else
  1469.         newObject = setDefaultAttributes(newObject)
  1470.         isValid, errMsg = validateAddOnObject(newObject)
  1471.         if not isValid then
  1472.           throwException("Object declared between line " .. newObjectLine .. " and " .. lineNumber .. " is invalid: " .. errMsg, fileName, lineNumber)
  1473.         else
  1474.           addOns[fileName][newObject.objectID] = newObject
  1475.           newObject = nil
  1476.           createNewObject = false
  1477.           newObjectLine = -1
  1478.         end
  1479.       end
  1480.     elseif (string.find(line, "=") and started) then -- Line defines an attribute.
  1481.       if not createNewObject then
  1482.         throwException("Declared an attribute without having an <object> tag!", fileName, lineNumber)
  1483.       else
  1484.         attribute, value = splitAt(line, "=")
  1485.         newObject[attribute] = value
  1486.       end
  1487.     elseif started then -- Throw an error if the line is inside the <objects> tags and if it's invalid. Otherwise keep looking for the <objects> tag.
  1488.       throwException("Line is neither a tag nor an attribute!", fileName, lineNumber)
  1489.     end
  1490.   end
  1491.  
  1492.   file.close()
  1493. end
  1494.  
  1495. -- Looks for AddOn files and adds them to the addOns-table.
  1496. function loadAddOns()
  1497.   files = fs.list(shell.dir())
  1498.   for key, file in pairs(files) do
  1499.     if not fs.isDir(file) then -- if "file" is an actual file
  1500.       if string.sub(file, -4) == addOnExtension then -- if the file is a valid AddOn-file
  1501.         readAddOn(file)
  1502.       end
  1503.     end
  1504.   end
  1505.  
  1506.   for tableKey, tableValue in pairs(addOns) do
  1507.     for key, value in pairs(tableValue) do
  1508.       table.insert(objectTypes, tableKey .. " - " .. key)
  1509.     end
  1510.   end
  1511. end
  1512.  
  1513. -- AddOn manager region end --
  1514.  
  1515. -- initialization
  1516.  
  1517. function init()
  1518.   if not getMonitor() then
  1519.     print("No monitor at " .. monitorSide .. " side!");
  1520.     return false
  1521.   end
  1522.  
  1523.   maxX, maxY = monitor.getSize()
  1524.   if (maxX < 16 or maxY < 10) then -- smaller than 2x2
  1525.     print("Screen too small! You need at least 2x2 monitors!")
  1526.     return false
  1527.   end
  1528.  
  1529.   loadAddOns()
  1530.   term.redirect(monitor)
  1531.  
  1532.   return true
  1533. end
  1534.  
  1535. function checkArgs()
  1536.   doCall = main
  1537.  
  1538.   if (args[1] ~= nil) then
  1539.     if (args[1] == "edit") then
  1540.       doCall = screenEditor
  1541.     end
  1542.   end
  1543.  
  1544.   doCall()
  1545. end
  1546.  
  1547. if init() then
  1548.   loadScreens()
  1549.   checkArgs()
  1550.  
  1551.   if editMode then
  1552.     saveScreens()
  1553.   end
  1554.  
  1555.   term.clear()
  1556.   term.setCursorPos(1, 1)
  1557.   term.restore()
  1558. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement