Advertisement
Encreedem

Graffiti v1.4

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