Encreedem

Graffiti v1.3

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