Advertisement
Encreedem

Graffiti v1.5

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