Encreedem

Graffiti v1.6

Sep 16th, 2013
407
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 69.51 KB | None | 0 0
  1. -- TODO: The Progress Bars movePos and scalePos get messed up when it's facing up.
  2.  
  3. local version = "1.6"
  4.  
  5. -- >>> Variables
  6.  
  7. -- Tables for users:
  8. local userFunctions = {}
  9. local userLists = {}
  10. local userInputs = {}
  11. local selectedItems = {}
  12. local selectedFiles = {}
  13.  
  14. --Monitor
  15. local monitor = nil
  16.  
  17. -- Text
  18. local text = {}
  19. text.back = " < "
  20. text.quit = " X "
  21. text.refresh = "Refresh"
  22. text.done = "Done"
  23. text.options = "Options"
  24. text.fileSelector = "[...]"
  25.  
  26. -- Colors
  27. local objectColors = {}
  28. local colorTheme = "Default"
  29.  
  30. -- Color Themes
  31. local colorThemes = {}
  32. colorThemes.Default = {
  33.   background = colors.black;
  34.   text = colors.white;
  35.   Button = { default = colors.red; active = colors.lime; text = colors.white };
  36.   ProgressBar = { low = colors.red; medium = colors.yellow; high = colors.lime; background = colors.gray; };
  37.   Input = { default = colors.white; text = colors.black; active = colors.yellow; };
  38.   List = { default = colors.blue; active = colors.lightBlue; };
  39.   FileSelector = { default = colors.red; text = colors.white; dir = colors.lime; file = colors.white; active = colors.lime; };
  40.   Editor = {
  41.     new = colors.white;
  42.     active = colors.lime;
  43.     move = colors.magenta;
  44.     scale = colors.pink;
  45.     marker = colors.gray;
  46.     alignmentTrue = colors.lime;
  47.     alignmentFalse = colors.red;
  48.   };
  49.   Container = {
  50.     Panel = {
  51.       border = colors.white;
  52.     };
  53.   };
  54. }
  55.  
  56. colorThemes["Windows CC"] = {
  57.   background = colors.white;
  58.   text = colors.black;
  59.   Button = { default = colors.lightGray; active = colors.lightBlue; text = colors.black };
  60.   ProgressBar = { low = colors.lime; medium = colors.lime; high = colors.lime; background = colors.lightGray; };
  61.   Input = { default = colors.lightGray; text = colors.black; active = colors.white; };
  62.   List = { default = colors.lightGray; active = colors.lightBlue; };
  63.   FileSelector = { default = colors.gray; text = colors.black; dir = colors.blue; file = colors.black; active = colors.lightBlue; };
  64.   Editor = {
  65.     new = colors.lightBlue;
  66.     active = colors.black;
  67.     move = colors.magenta;
  68.     scale = colors.pink;
  69.     marker = colors.gray;
  70.     alignmentTrue = colors.lime;
  71.     alignmentFalse = colors.red;
  72.   };
  73.   Container = {
  74.     Panel = {
  75.       border = colors.lightGray;
  76.     };
  77.   };
  78. }
  79.  
  80. for key, value in pairs(colorThemes[colorTheme]) do
  81.   objectColors[key] = value
  82. end
  83.  
  84. -- Sizes
  85. local size = {}
  86. size.Button = { width = 10; height = 3; }
  87. size.ProgressBar = { length = 10; }
  88. size.Container = { width = 20; height = 10; }
  89.  
  90. -- Save File
  91. local saveFileName = "Graffiti.sav"
  92.  
  93. -- API
  94. local initDone = false
  95. local isAPI = false -- Determines whether the program has been loaded as an API
  96. local variableValues = {}
  97. local progressBarValues = {}
  98.  
  99. -- Editor Options
  100. local editMode = false
  101. local showEditorOptions = false
  102. local editActions = { "Design", "Attributes", "Delete" }
  103. local lastWindow = "mainWindow"
  104. local editorFunctions = {}
  105. local rightClickActions = {"Attributes", "Delete" }
  106.  
  107. -- tables
  108. local args = { ... }
  109. local sides = { "left", "top", "right", "bottom", "front", "back" }
  110. local objectTypes = { "Button", "Text", "Variable", "ProgressBar", "Input", "List", "Panel" }
  111. local eventTypes = { "quit", "button_clicked", "button_toggled", "selection_changed", "text_changed" }
  112. local objects = {}
  113. local windows = { children = { mainWindow = { objType = "Window", children = {} } } }
  114. local windowBuffer = nil
  115. local defaultButtons = {}
  116.  
  117. -- Other
  118. local quit = false
  119. local maxX, maxY = 51, 19
  120. local out = term -- Output: either "term" or the monitor
  121. local outIsTerm = false
  122. local autoLoadObjects = true
  123. local changeButtonColor = true
  124. local currentWindow = "mainWindow"
  125.  
  126. -- >> User Functions
  127.  
  128. -- user variables
  129. local randomValue= 50
  130.  
  131. -- user functions
  132.  
  133. function userFunctions.test()
  134.   sleep(1)
  135. end
  136.  
  137. function userFunctions.setRandomValue()
  138.   randomValue = math.random(100)
  139. end
  140.  
  141. function userFunctions.refresh()
  142.   drawWindow()
  143. end
  144.  
  145. -- user lists
  146.  
  147. userLists.testList = {
  148.   "Testitem 1",
  149.   "Testitem 2",
  150.   "Testitem 3"
  151. }
  152.  
  153. -- Define the value of a variable-object.
  154. function getVariableValue(object)
  155.   assert(object)
  156.   local objID = object.objID
  157.  
  158.   if (objID == "testVariable") then
  159.     return "Variable";
  160.   elseif (objID == "Time") then
  161.     return textutils.formatTime(os.time(), true)
  162.   else
  163.     return variableValues[objID]
  164.   end
  165. end
  166.  
  167. -- Definie the value of a progressBar-object
  168. -- 0: empty; 100: full
  169. function getProgressBarValue(object)
  170.   assert(object)
  171.   local objID = object.objID
  172.  
  173.   if (objID == "testProgressBar") then
  174.     return 87
  175.   elseif (objID == "randomProgressBar") then
  176.     return randomValue
  177.   else
  178.     return progressBarValues[objID]
  179.   end
  180. end
  181.  
  182. --[[ WARNING!
  183. Everything below this comment
  184. shouldn't be edited!
  185. If you do so and the program doesn't work anymore
  186. then it's your fault!
  187. ]]
  188.  
  189. --[[ Displays the text, the content of a table
  190. or a star in the upper left corner until you press
  191. a key.]]
  192. function dBug(text)
  193.   out.setCursorPos(1, 1)
  194.  
  195.   if (text == nil) then
  196.     out.write("*")
  197.     getKeyInput()
  198.     out.setCursorPos(1, 1)
  199.     out.write(" ")
  200.     return
  201.   elseif (type(text) == "table") then
  202.     for key, value in pairs(text) do
  203.       print(key .. ": " .. tostring(value))
  204.     end
  205.   else
  206.     out.write(text)
  207.   end
  208.  
  209.   getKeyInput()
  210. end
  211.  
  212. -- >>> Shortcut functions (for key inputs)
  213. -- Not yet implemented! WIP!
  214. -- TODO: Fix bug where the key event won't disappear from the queue.
  215.  
  216. function callShortcut(key)
  217.   --if (key and shortcut[key]) then
  218.   --  shortcut[key]()
  219.   --end
  220. end
  221.  
  222. shortcut = {}
  223.  
  224. -- s
  225. shortcut[31] = function()
  226.   out.clear()
  227.   out.setCursorPos(1, 1)
  228.   print("Saving windows...")
  229.  
  230.   saveWindows()
  231.  
  232.   print("Windows saved!")
  233.   print("Press any key to continue...")
  234. end
  235.  
  236. -- q
  237. shortcut[16] = function()
  238.   quit = true
  239. end
  240.  
  241. -- >>> Object helper functions
  242.  
  243. function clearScreen()
  244.   out.setBackgroundColor(objectColors.background)
  245.   out.setTextColor(objectColors.text)
  246.   out.clear()
  247.   out.setCursorPos(1, 1)
  248. end
  249.  
  250. function initDefaultButtons()
  251.   defaultButtons.quit = {
  252.     text=text.quit,
  253.     left=maxX - string.len(text.quit) + 1,
  254.     top=1,
  255.     right=maxX,
  256.     bottom=1,
  257.     required = function()
  258.       return true
  259.     end
  260.   }
  261.  
  262.   defaultButtons.back = {
  263.     text = text.back,
  264.     left = 1,
  265.     top = 1,
  266.     right = string.len(text.back),
  267.     bottom = 1,
  268.     required = function()
  269.       return getCurrentWindow().showBackButton
  270.     end
  271.   }
  272.  
  273.   defaultButtons.refresh = {
  274.     text = text.refresh,
  275.     left = maxX - string.len(text.refresh) + 1,
  276.     top = maxY,
  277.     right = maxX,
  278.     bottom = maxY,
  279.     required = function()
  280.       return (getCurrentWindow().showRefreshButton or (editMode and not showEdtorOptions))
  281.     end
  282.   }
  283.  
  284.   defaultButtons.options = {
  285.     text = text.options,
  286.     left = 1,
  287.     top = maxY,
  288.     right = string.len(text.options),
  289.     bottom = maxY,
  290.     required=function()
  291.       return (editMode and not showEditorOptions)
  292.     end
  293.   }
  294. end
  295.  
  296. -- Returns whether the button with the given name
  297. -- has been pressed.
  298. function defaultButtonPressed(name, x, y)
  299.   assert(name)
  300.   assert(x)
  301.   assert(y)
  302.  
  303.   local window = getCurrentWindow()
  304.  
  305.   if (defaultButtons[name]) then
  306.     local button = defaultButtons[name]
  307.     if (x >= button.left and x <= button.right and y >= button.top and y <= button.bottom) then
  308.       return (button.required() or (editMode and not showEditorOptions and currentWindow ~= "mainWindow"))
  309.     end
  310.   else
  311.     return false
  312.   end
  313. end
  314.  
  315. -- Returns the window object that is currently
  316. -- displayed.
  317. function getCurrentWindow()
  318.   if showEditorOptions then
  319.     return editorWindows.children[currentWindow]
  320.   else
  321.     return windows.children[currentWindow]
  322.   end
  323. end
  324.  
  325. -- Used by the List and Selector objects to
  326. -- determine how wide the list should be.
  327. function getLongestString(strings)
  328.   if (strings == nil or #strings == 0) then
  329.     return 0
  330.   end
  331.  
  332.   local ret = 0
  333.  
  334.   for key, value in pairs(strings) do
  335.     length = string.len(value)
  336.     if (length > ret) then
  337.       ret = length
  338.     end
  339.   end
  340.  
  341.   return ret
  342. end
  343.  
  344. -- Checks whether dir is a valid direction-string.
  345. function isValidDirection(dir)
  346.   if (dir ~= nil and
  347.      (dir == "left" or
  348.       dir == "up" or
  349.       dir == "right" or
  350.       dir == "down")) then
  351.     return true
  352.   end
  353.  
  354.   return false
  355. end
  356.  
  357. --[[ Returns a table containing tables for all
  358. files and directories in the given path:
  359. Returns: fileName, filePath, isDir
  360. ]]
  361. function getFileList(path)
  362.   local ret = {}
  363.   local dirList = {}
  364.   local fileList = {}
  365.  
  366.   for file in fs.list(path) do
  367.     if (fs.isDir(file)) then
  368.       table.insert(dirList, dirIndex, file)
  369.       dirIndex = dirIndex + 1
  370.     else
  371.       table.insert(fileList, fileIndex, file)
  372.       fileIndex = fileIndex + 1
  373.     end
  374.   end
  375.   for _, file in ipairs(dirList) do
  376.     table.insert(list, {fileName=file, filePath=fs.combine(path, file), isDir=true})
  377.   end
  378.   for _, file in ipairs(fileList) do
  379.     table.insert(list, {fileName=file, filePath=fs.combine(path, file), isDir=false})
  380.   end
  381.  
  382.   return ret
  383. end
  384.  
  385. -- Returns "horizontal" or "vertical" depending on
  386. -- the given direction.
  387. function getOrientation(direction)
  388.   local orientation = nil
  389.  
  390.   if (direction == "left" or direction == "right") then
  391.     orientation = "horizontal"
  392.   elseif (direction == "up" or direction == "down") then
  393.     orientation = "vertical"
  394.   end
  395.  
  396.   return orientation
  397. end
  398.  
  399. function eventTypeExists(eventType)
  400.   if (eventType == nil) then
  401.     return true
  402.   end
  403.  
  404.   for _, event in pairs(eventTypes) do
  405.     if (event == eventType) then
  406.       return true
  407.     end
  408.   end
  409.  
  410.   return false
  411. end
  412.  
  413. -- >>> Path
  414.  
  415. Path = {}
  416. function Path:getParent()
  417.   return Path.getContainerAt(self, #self - 1)
  418. end
  419.  
  420. function Path:getContainerAt(nestLevel)
  421.   local container = getCurrentWindow()
  422.  
  423.   for i = 1, nestLevel do
  424.     if (container.children[self[i]] and container.children[self[i]].isContainer) then
  425.       container = container.children[self[i]]
  426.     else
  427.       return nil -- NOTE: was "break" before
  428.     end
  429.   end
  430.  
  431.   return container
  432. end
  433.  
  434. function Path:getObject()
  435.   local container = Path.getContainerAt(self, #self - 1)
  436.   return container.children[self[#self]]
  437. end
  438.  
  439. -- Returns the coordinates which are relative to
  440. -- the last container represented by the path.
  441. function Path:getRelativePos(x, y, checkFullPath)
  442.   local container
  443.   local limit = checkFullPath and #self or #self - 1
  444.  
  445.   for i = 1, limit do
  446.     container = Path.getContainerAt(self, i)
  447.    
  448.     if container then
  449.       x, y = objects.Container.getRelativePos(container, x, y)
  450.       --if not (container.children[self[i + 1]] and container.children[self[i + 1]].isContainer) then
  451.       --  break
  452.       --end
  453.     else
  454.       break
  455.     end
  456.   end
  457.  
  458.   return x, y
  459. end
  460.  
  461. --[[ Generates a table of keys representing the
  462. nesting of the containers that have been clicked.
  463. Returns: e.g. {containeID, subContainerID}]]
  464. Path.getContainerPath = function(x, y)
  465.   local containerPath = {}
  466.   local currentContainer = getCurrentWindow()
  467.   local finished = false
  468.   local nestLevel = 1
  469.  
  470.   while not finished do
  471.     finished = true -- Set to true so that the loop stops when no container is found.
  472.     for objectID, object in pairs(currentContainer.children) do
  473.       if finished and object.isContainer then
  474.         if (objects.Container.contentAreaClicked(object, x, y)) then
  475.           containerPath[nestLevel] = objectID
  476.           currentContainer = currentContainer.children[objectID]
  477.           x, y = objects.Container.getRelativePos(currentContainer, x, y)
  478.           finished = false
  479.         end
  480.       end
  481.     end
  482.    
  483.     nestLevel = nestLevel + 1
  484.   end
  485.  
  486.   return containerPath
  487. end
  488.  
  489. -- >>> Buffer
  490.  
  491. Buffer = {
  492.   bufferTable = {};
  493.   width = 0;
  494.   height = 0;
  495. }
  496.  
  497. function Buffer.newPixel(backCol, textCol, char, path)
  498.   local ret = {}
  499.   ret.background = backCol or objectColors.background
  500.   ret.text = textCol or objectColors.text
  501.   ret.char = (type(char) == "string" and char ~= "") and char or " "
  502.   ret.path = path or nil
  503.  
  504.   return ret;
  505. end
  506.  
  507. function Buffer:new(object)
  508.   object = object or {}
  509.   setmetatable(object, self)
  510.   self.__index = self
  511.   return object
  512. end
  513.  
  514. function Buffer:init(width, height, path, backCol, textCol)
  515.   self.width = width or error("Can't initialize buffer because the width didn't get specified!")
  516.   self.height = height or error("Can't initialize buffer because the height didn't get specified!")
  517.   self.bufferTable = {}
  518.  
  519.   for col = 1, width do
  520.     self.bufferTable[col] = {}
  521.     for row = 1, height do
  522.       self.bufferTable[col][row] = self.newPixel(backCol, textCol, " ", path)
  523.     end
  524.   end
  525. end
  526.  
  527. function Buffer:trim(left, top, right, bottom)
  528.   left = (left < 0) and 0 or left
  529.   top = (top < 0) and 0 or top
  530.   right = (right < 0) and 0 or right
  531.   bottom = (bottom < 0) and 0 or bottom
  532.  
  533.   local width = self.width - left - right
  534.   local height = self.height - top - bottom
  535.   local trimmed = Buffer:new()
  536.   trimmed:init(width, height)
  537.  
  538.   for col = 1, width do
  539.     for row = 1, height do
  540.       trimmed:setPixel(col, row, self.bufferTable[left + col][top + row])
  541.     end
  542.   end
  543.  
  544.   return trimmed
  545. end
  546.  
  547. function Buffer:draw(x, y)
  548.   x = x or 1
  549.   y = y or 1
  550.   local currentPixel
  551.  
  552.   for col = 0, self.width - 1 do
  553.     for row = 0, self.height - 1 do
  554.       currentPixel = self.bufferTable[col+1][row+1]
  555.       out.setCursorPos(x + col, y + row)
  556.       out.setBackgroundColor(currentPixel.background)
  557.       out.setTextColor(currentPixel.text)
  558.       out.write(currentPixel.char)
  559.     end
  560.   end
  561. end
  562.  
  563. function Buffer:setBackgroundColor(color)
  564.   for col = 1, width do
  565.     for row = 1, height do
  566.       self.bufferTable[col][row].background = color
  567.     end
  568.   end
  569. end
  570.  
  571. function Buffer:setTextColor(color)
  572.   for col = 1, width do
  573.     for row = 1, height do
  574.       self.bufferTable[col][row].text = color
  575.     end
  576.   end
  577. end
  578.  
  579. function Buffer:setPixel(x, y, pixel)
  580.   assert(x)
  581.   assert(y)
  582.   assert(pixel)
  583.   if (x > 0 and x <= self.width and y > 0 and y <= self.height) then
  584.     for key, value in pairs(pixel) do
  585.       if value then
  586.         self.bufferTable[x][y][key] = value
  587.       end
  588.     end
  589.   end
  590. end
  591.  
  592. function Buffer:addText(x, y, text)
  593.   assert(x)
  594.   assert(y)
  595.   assert(text)
  596.  
  597.   for char in text:gmatch(".") do
  598.     self:setPixel(x, y, { char=char })
  599.     x = x + 1
  600.   end
  601. end
  602.  
  603. function Buffer:addBuffer(x, y, buffer)
  604.   assert(x)
  605.   assert(y)
  606.   assert(buffer)
  607.   for col = 1, buffer.width do
  608.     for row = 1, buffer.height do
  609.       self:setPixel(x + col - 1, y + row - 1, buffer.bufferTable[col][row])
  610.     end
  611.   end
  612. end
  613.  
  614. -- >>> Objects
  615.  
  616. objects = {}
  617. -- Returns a new object with its default attributes
  618. -- depending on the object type.
  619. function objects.new(objectType, x, y)
  620.   local object = {}
  621.   local path = Path.getContainerPath(x, y)
  622.   local relativeX, relativeY = Path.getRelativePos(path, x, y, true)
  623.  
  624.   object.objID = "new" .. objectType
  625.   object.objType = objectType
  626.   object.path = path
  627.   object.x = relativeX
  628.   object.y = relativeY
  629.   object.absoluteX = x
  630.   object.absoluteY = y
  631.  
  632.   local maxWidth = maxX - x
  633.   local maxHeight = maxY - y
  634.  
  635.   if (objects[objectType] and objects[objectType].new) then
  636.     objects[objectType].new(object, maxWidth, maxHeight)
  637.   elseif (objects.Container[objectType]) then
  638.     objects.Container.new(object, maxWidth, maxHeight)
  639.   else
  640.     objects["Unknown"].new(objectType)
  641.   end
  642.  
  643.   return object
  644. end
  645.  
  646. function objects.create(objType, x, y)
  647.   local object = objects.new(objType, x, y)
  648.   local container = Path.getContainerAt(object.path, #object.path)
  649.   local key = objects.Container.getNextFreeKey(container)
  650.   table.insert(object.path, key)
  651.   table.insert(container.children, key, object)
  652.  
  653.   windowBuffer:addBuffer(x, y, objects.get(object))
  654.   objects.draw(object)
  655. end
  656.  
  657. -- Returns the buffer of the object.
  658. function objects.get(self, param)
  659.   local objType = self.objType or "Unknown"
  660.   if (self.isContainer) then
  661.     return objects.Container.get(self, param)
  662.   elseif (objects[objType] and objects[objType].get) then
  663.     return objects[objType].get(self, param)
  664.   else
  665.     error("Can't get buffer of object type \"" .. objType .. "\"!")
  666.   end
  667. end
  668.  
  669. function objects.remove(self)
  670.   local path = self.path
  671.   local objKey = path[#path]
  672.   local container = Path.getContainerAt(self.path, #self.path - 1)
  673.  
  674.   container.children[objKey] = nil
  675.  
  676.   for i = #path - 2, 0, -1 do
  677.     local parentContainer = Path.getContainerAt(self.path, i)
  678.     parentContainer.children[path[i + 1]] = container
  679.     container = parentContainer
  680.   end
  681.  
  682.   windows.children[currentWindow] = container
  683. end
  684.  
  685. -- Draws the object.
  686. function objects.draw(self, param)
  687.   local objType = self.objType
  688.  
  689.   if (#self.path > 1) then
  690.     local container = getCurrentWindow()
  691.     local x, y = self.absoluteX, self.absoluteY
  692.     objects.Container.drawBuffer(container, objects.get(self, param), self.path, x, y, x, y, 0)
  693.   else
  694.     local buffer = objects.get(self, param)
  695.     buffer:draw(self.x, self.y)
  696.   end
  697. end
  698.  
  699. -- Returns the resulting event and a "params"-array.
  700. function objects.click(self, x, y)
  701.   local objType = self.objType
  702.   local event, params
  703.   if objects[objType] and objects[objType].click then
  704.     event, params = objects[objType].click(self, x, y)
  705.   elseif (self.isContainer and objects.Container[objType] and objects.Container[objType].click) then
  706.     event, params = objects.Container[objType].click(self, x, y)
  707.   end
  708.  
  709.   return event, params
  710. end
  711.  
  712. -- Moves the object.
  713. function objects.move(self, addX, addY)
  714.   assert(self)
  715.  
  716.   local objType = self.objType
  717.  
  718.   if (objects[objType] and objects[objType].move) then
  719.     objects[objType].move(self, addX, addY)
  720.   elseif (self.isContainer) then
  721.     self.x = self.x + addX
  722.     self.y = self.y + addY
  723.     objects.Container.move(self, addX, addY)
  724.   else
  725.     self.x = self.x + addX
  726.     self.y = self.y + addY
  727.     self.absoluteX = self.absoluteX + addX
  728.     self.absoluteY = self.absoluteY + addY
  729.   end
  730. end
  731.  
  732. -- Scales the object.
  733. function objects.scale(self, x, y)
  734.   assert(self)
  735.   local objType = self.objType
  736.  
  737.   if (objects[objType] and objects[objType].scale) then
  738.     objects[objType].scale(self, x, y)
  739.   elseif (self.isContainer and objects.Container[objType].scale) then
  740.     objects.Container[objType].scale(self, x, y)
  741.   else
  742.     local objX, objY = self.x, self.y
  743.     if (x > objX and y >= objY) then
  744.       self.width = x - objX + 1
  745.       self.height = y - objY + 1
  746.     end
  747.   end
  748. end
  749.  
  750. --[[ Returns the position of the pixel which shows
  751. the user where to click when he wants to move the
  752. object.]]
  753. function objects.getMovePos(self)
  754.   local objType = self.objType
  755.   if (objects[objType] and objects[objType].getMovePos) then
  756.     return objects[objType].getMovePos(self)
  757.   elseif (self.isContainer and objects.Container[objType] and objects.Container[objType].getMovePos) then
  758.     return objects.Container[objType].getMovePos(self)
  759.   else
  760.     return self.absoluteX, self.absoluteY
  761.   end
  762. end
  763.  
  764. --[[ Returns the position of the pixel which shows
  765. the user where to click when he wants to scale the
  766. object.]]
  767. function objects.getScalePos(self)
  768.   if not self.canScale then
  769.     error("Tried to get scale pos of an unscalable object!")
  770.   end
  771.  
  772.   local objType = self.objType
  773.   if (objects[objType] and objects[objType].getScalePos) then
  774.     return objects[objType].getScalePos(self)
  775.   elseif (self.isContainer and objects.Container[objType].getScalePos) then
  776.     objects.Container[objType].getScalePos(self)
  777.   else
  778.     return self.absoluteX + self.width - 1, self.absoluteY + self.height - 1
  779.   end
  780. end
  781.  
  782. -- Returns the left, top, right and bottom coordinates of the object.
  783. function objects.getDimensions(self)
  784.   local objType = self.objType
  785.   if (objects[objType] and objects[objType].getDimensions) then
  786.     return objects[objType].getDimensions(self)
  787.   elseif (self.isContainer and objects.Container[objType] and objects.Container[objType].getDimensions) then
  788.     return objects.Container[objType].getDimensions(self)
  789.   else
  790.     local left, top, right, bottom
  791.     left, top = self.x, self.y
  792.     right = left + self.width - 1
  793.     bottom = top + self.height - 1
  794.     return left, top, right, bottom
  795.   end
  796. end
  797.  
  798. -- >> Unknown
  799. objects.Unknown = {}
  800. objects.Unknown.new = function(objType)
  801.   objType = objType and " \"" .. objType .. "\"" or ""
  802.   error("Tried to create new object with unknown object ID" .. objType .. "!")
  803. end
  804.  
  805. objects.Unknown.get = function(self)
  806.   error("Tried to get the buffer of and unknown object!")
  807. end
  808.  
  809. objects.Unknown.draw = function(self)
  810.   assert(self, "Draw function got called with nil parameter!")
  811.  
  812.   local x = self.x
  813.   local y = self.y
  814.  
  815.   if (x and y) then
  816.     out.setCursorPos(x, y)
  817.     out.write("?")
  818.   else
  819.     error("Tried to draw invalid object!")
  820.   end
  821. end
  822.  
  823. -- >> Button
  824. objects.Button = {}
  825. objects.Button.new = function(self, maxWidth, maxHeight)
  826.   self.width = (maxWidth < size.Button.width) and maxWidth or size.Button.width
  827.   self.height = (maxHeight < size.Button.height) and maxHeight or size.Button.height
  828.   self.widthPercent = maxX / self.width
  829.   self.heightPercent = maxY / self.height
  830.   self.text = "Button"
  831.   self.funcType = ""
  832.   self.param = ""
  833.   self.canScale = true
  834.   self.canClick = true
  835. end
  836.  
  837. objects.Button.get = function(self, buttonColor)
  838.   local width = self.width
  839.   local height = self.height
  840.   local path = self.path
  841.   local text = self.text
  842.   local color = buttonColor or objectColors.Button.default
  843.  
  844.   local buffer = Buffer:new()
  845.   local textCol = math.floor((width - string.len(text)) / 2) + 1
  846.   local textRow = math.ceil(height / 2)
  847.   buffer:init(width, height, path, color, objectColors.Button.text)
  848.   buffer:addText(textCol, textRow, text)
  849.  
  850.   return buffer
  851. end
  852.  
  853. -- Returns: "button_clicked" event, objID
  854. objects.Button.click = function(self, x, y)
  855.   assert(self)
  856.  
  857.   local actionType = self.funcType
  858.   local param = self.param
  859.  
  860.   if (actionType == "switch") then
  861.     drawWindow(param)
  862.     return nil, nil
  863.   elseif (actionType == "function") then
  864.     if changeButtonColor then
  865.       objects.draw(self, objectColors.Button.active)
  866.     end
  867.    
  868.     if userFunctions[param] then
  869.       userFunctions[param]()
  870.     elseif editorFunctions[param] then
  871.       editorFunctions[param]()
  872.     end
  873.    
  874.     if changeButtonColor then
  875.       objects.draw(self, objectColors.Button.default)
  876.     else
  877.       changeButtonColor = true
  878.     end
  879.    
  880.     return "button_clicked", {self.objID}
  881.   end
  882. end
  883.  
  884. -- >> Text
  885. objects.Text = {}
  886. objects.Text.new = function(self)
  887.   self.text = "Text"
  888.   self.width = #self.text
  889.   self.height = 1
  890. end
  891.  
  892. objects.Text.get = function(self)
  893.   local width = #self.text
  894.   local buffer = Buffer:new()
  895.   buffer:init(width, 1, self.path)
  896.   buffer:addText(1, 1, self.text)
  897.  
  898.   return buffer
  899. end
  900.  
  901. objects.Text.draw = function(self)
  902.   local x = self.x
  903.   local y = self.y
  904.   local text = self.text
  905.   out.setCursorPos(x, y)
  906.   out.write(text)
  907. end
  908.  
  909. objects.Text.getDimensions = function(self)
  910.   local left, top, right, bottom = self.x, self.y, 1, self.y
  911.   right = left + string.len(self.text) - 1
  912.   return left, top, right, bottom
  913. end
  914.  
  915. -- >> Variable
  916. objects.Variable = {}
  917. objects.Variable.new = function(self)
  918.   self.width = 1
  919.   self.height = 1
  920. end
  921.  
  922. objects.Variable.get = function(self, value)
  923.   local buffer = Buffer:new()
  924.  
  925.   if value then
  926.     buffer:init(string.len(value), 1, self.path)
  927.     buffer:addText(1, 1, value)
  928.   else
  929.     buffer:init(1, 1, self.path)
  930.   end
  931.  
  932.   return buffer
  933. end
  934.  
  935. -- >> Progress Bar
  936. objects.ProgressBar = {}
  937. objects.ProgressBar.new = function(self, maxWidth)
  938.   self.length = (maxWidth < size.ProgressBar.length) and maxWidth or size.ProgressBar.length
  939.   self.lengthPercent = maxX / self.length
  940.   self.width = self.length
  941.   self.height = 1
  942.   self.direction = "right"
  943.   self.objID = "newProgressBar"
  944.   self.canScale = true
  945. end
  946.  
  947. objects.ProgressBar.get = function(self, value)
  948.   local length = self.length
  949.   local direction = (isValidDirection(self.direction)) and self.direction or "right"
  950.   local orientation = getOrientation(direction) or error("Direction " .. direction .. " is invalid!")
  951.   value = value or progressBarValues[self.objID]
  952.  
  953.   if (orientation == "horizontal") then
  954.     width, height = length, 1
  955.   else
  956.     width, height = 1, length
  957.   end
  958.  
  959.   local buffer = Buffer:new()
  960.   buffer:init(width, height, self.path, objectColors.ProgressBar.background)
  961.  
  962.   if value then
  963.     local color
  964.     if (value < 33) then
  965.       color = objectColors.ProgressBar.low
  966.     elseif (value > 66) then
  967.       color = objectColors.ProgressBar.high
  968.     else
  969.       color = objectColors.ProgressBar.medium
  970.     end
  971.    
  972.     local filled = math.floor((length / 100) * value)
  973.     local valueBuffer = objects.Line.get(getOrientation(direction), filled, color)
  974.     local addX, addY = 1, 1
  975.    
  976.     if (direction == "left") then
  977.       addX = width - filled
  978.     elseif (direction == "up") then
  979.       addY = height - filled
  980.     end
  981.    
  982.     buffer:addBuffer(addX, addY, valueBuffer)
  983.   end
  984.  
  985.   return buffer
  986. end
  987.  
  988. objects.ProgressBar.getMovePos = function(self)
  989.   local dir = self.direction
  990.   local length = self.length
  991.   local x, y = self.absoluteX, self.absoluteY
  992.  
  993.   if (dir == "left") then
  994.     return x + length - 1, y
  995.   elseif (dir == "up") then
  996.     return x, y + length - 1
  997.   else
  998.     return x, y
  999.   end
  1000. end
  1001.  
  1002. objects.ProgressBar.getScalePos = function(self)
  1003.   local dir = self.direction
  1004.   local length = self.length
  1005.   local x, y = self.absoluteX, self.absoluteY
  1006.  
  1007.   if (dir == "right") then
  1008.     return x + length - 1, y
  1009.   elseif (dir == "down") then
  1010.     return x, y + length - 1
  1011.   else
  1012.     return x, y
  1013.   end
  1014. end
  1015. -- Move and scale pos is wrong when the progress bar goes up.
  1016. objects.ProgressBar.scale = function(self, x, y)
  1017.   local moveX, moveY = objects.getMovePos(self)
  1018.   local length
  1019.   local newX, newY
  1020.   local direction
  1021.   local width, height
  1022.  
  1023.   if (x < moveX and y == moveY) then -- Clicked left of the progressBar.
  1024.     length = moveX - x + 1
  1025.     newX, newY = x, y
  1026.     direction = "left"
  1027.     width, height = length, 1
  1028.   elseif (x == moveX and y < moveY) then -- Clicked above the progressBar.
  1029.     length = moveY - y + 1
  1030.     newX, newY = x, y
  1031.     direction = "up"
  1032.     width, height = 1, length
  1033.   elseif (x > moveX and y == moveY) then -- Clicked right of the progressBar.
  1034.     length = x - moveX + 1
  1035.     direction = "right"
  1036.     newX, newY = moveX, moveY
  1037.     width, height = length, 1
  1038.   elseif (x == moveX and y > moveY) then -- Clicked below the progressBar.
  1039.     length = y - moveY + 1
  1040.     direction = "down"
  1041.     newX, newY = moveX, moveY
  1042.     width, height = 1, length
  1043.   else
  1044.     return
  1045.   end
  1046.  
  1047.   if (length > 2) then
  1048.     self.absoluteX, self.absolutey = newX, newY
  1049.     self.x, self.y = Path.getRelativePos(self.path, newX, newY)
  1050.     self.direction = direction
  1051.     self.width, self.height = width, height
  1052.     self.length = length
  1053.     self.lengthPercent = length / maxX
  1054.   end
  1055. end
  1056.  
  1057. -- >> Input
  1058. objects.Input = {}
  1059. objects.Input.new = function(self)
  1060.   self.message = "Enter something."
  1061.   self.isPassword = false
  1062.   self.width = 2
  1063.   self.height = 1
  1064.   self.canClick = true
  1065. end
  1066.  
  1067. objects.Input.get = function(self)
  1068.   local userInput = userInputs[self.objID] or ""
  1069.   local width, height = 2 + string.len(userInput), 1
  1070.  
  1071.   local buffer = Buffer:new()
  1072.   buffer:init(width, height, self.path, objectColors.Input.default)
  1073.   if userInput ~= "" then
  1074.     buffer:addText(userInput)
  1075.   end
  1076.  
  1077.   return buffer
  1078. end
  1079.  
  1080. -- Returns: "text_changed" event, objID, text
  1081. objects.Input.click = function(self)
  1082.   local x = self.x
  1083.   local y = self.y
  1084.   local objID = self.objID
  1085.   local message = self.message
  1086.   local isPassword = (self.isPassword == nil) and false or self.isPassword
  1087.   local maxLength = self.maxLength
  1088.   local existingInput = userInputs[objID]
  1089.  
  1090.   out.setBackgroundColor(objectColors.background)
  1091.   out.setCursorPos(x, y)
  1092.   if (existingInput ~= nil) then -- Clear the text on the input object.
  1093.     out.write(string.rep(" ", string.len(existingInput) + 2))
  1094.   else
  1095.     out.write("  ")
  1096.   end
  1097.   userInputs[objID] = nil
  1098.  
  1099.   out.setCursorPos(x, y)
  1100.   if not outIsTerm then
  1101.     -- make the input-object yellow
  1102.     out.setBackgroundColor(objectColors["Input"].active)
  1103.     out.write("  ")
  1104.     out.setBackgroundColor(objectColors.background)
  1105.   end
  1106.  
  1107.   if outIsTerm then
  1108.     out.setCursorPos(x + 1, y)
  1109.   end
  1110.  
  1111.   local userInput = readUserInput(message, isPassword)
  1112.   if (userInput ~= nil) then
  1113.     userInputs[objID] = userInput
  1114.   end
  1115.  
  1116.   out.setCursorPos(x, y)
  1117.   out.setBackgroundColor(objectColors.Input.default)
  1118.   out.setTextColor(objectColors.Input.text)
  1119.  
  1120.   out.write(" ")
  1121.   if (userInput ~= nil and userInput ~= "") then
  1122.     if isPassword then
  1123.       for i = 1, string.len(userInput) do
  1124.         out.write("*")
  1125.       end
  1126.     else
  1127.       out.write(userInput)
  1128.     end
  1129.   end
  1130.  
  1131.   out.write(" ")
  1132.   out.setBackgroundColor(objectColors.background)
  1133.   out.setTextColor(objectColors.text)
  1134.  
  1135.   -- NOTE: Changed "return ret" to this one:
  1136.   return "text_changed", {self.objID, userInput}
  1137. end
  1138.  
  1139. -- >> List
  1140. objects.List = {}
  1141. objects.List.new = function(self)
  1142.   self.elements = userLists.testList
  1143.   self.objID = "testList"
  1144.   self.isMultiselect = false
  1145.   self.canClick = true
  1146. end
  1147.  
  1148. objects.List.get = function(self)
  1149.   local buffer = Buffer:new()
  1150.   local elements = self.elements or { "empty" }
  1151.  
  1152.   if (type(elements) == "string") then
  1153.     elements = { [1]=self.elements }
  1154.   end
  1155.  
  1156.   -- If there's a list which has this list's first element as its key.
  1157.   if (#elements == 1 and userLists[elements[1]]) then
  1158.     self.elements = userLists[self.elements[1]]
  1159.   end
  1160.  
  1161.   elements = self.elements
  1162.  
  1163.   local width = getLongestString(elements) + 2
  1164.   self.width = width
  1165.   local height = #elements
  1166.   self.height = height
  1167.   local objID = self.objID
  1168.   local isMultiselect = self.isMultiselect
  1169.   if not selectedItems[objID] then
  1170.     selectedItems[objID] = {}
  1171.   end
  1172.  
  1173.   buffer:init(width, height, self.path, objectColors.List.default)
  1174.  
  1175.   local line = 1
  1176.   for key, element in pairs(elements) do
  1177.     if (selectedItems[objID][key]) then
  1178.       buffer:addBuffer(1, line, objects.Line.get("horizontal", width, objectColors.List.active))
  1179.     end
  1180.     buffer:addText(2, line, elements[line])
  1181.     line = line + 1
  1182.   end
  1183.  
  1184.   return buffer
  1185. end
  1186.  
  1187. -- Returns: "selection_changed" event, objID, key, true or false
  1188. objects.List.click = function(self, x, y)
  1189.   local objID = self.objID
  1190.   local isMultiselect = self.isMultiselect
  1191.   local itemSelected = selectedItems[objID][y]
  1192.  
  1193.   if (isMultiselect) then
  1194.     selectedItems[objID][y] = not itemSelected
  1195.   else
  1196.     selectedItems[objID] = {}
  1197.     selectedItems[objID][y] = true
  1198.   end
  1199.  
  1200.   objects.draw(self)
  1201.  
  1202.   return "selection_changed", {self.objID, y, selectedItems[objID][y]}
  1203. end
  1204.  
  1205. objects.List.getFirstSelectedKey = function(self)
  1206.   local objID = self.objID
  1207.  
  1208.   for key, value in pairs(selectedItems[objID]) do
  1209.     if (value == true) then
  1210.       return key
  1211.     end
  1212.   end
  1213.  
  1214.   return nil
  1215. end
  1216.  
  1217. -- >> File Selector
  1218. objects.FileSelector = {}
  1219. objects.FileSelector.new = function(self)
  1220.   self.width = string.len(text.fileSelector)
  1221.   self.height = 1
  1222.   self.isMultiselect = false
  1223.   self.canClick = true
  1224. end
  1225.  
  1226. objects.FileSelector.draw = function(self)
  1227.   local objectID = objectID
  1228.   local x = self.x
  1229.   local y = self.y
  1230.   local isMultiselect = self.isMultiselect
  1231.  
  1232.   out.setBackgroundColor(objectColors["FileSelector"].default)
  1233.   out.setTextColor(objectColors["FileSelector"].text)
  1234.  
  1235.   out.setCursorPos(x, y)
  1236.   out.write(text.fileSelector)
  1237.  
  1238.   if (selectedFiles[objectID] ~= nil) then
  1239.     out.setBackgroundColor(objectColors.background)
  1240.     out.setTextColor(objectColors.text)
  1241.     local files = selectedFiles[objectID]
  1242.     out.write(" ")
  1243.     if (type(files) == "table") then
  1244.       local sep = ""
  1245.       for _, fileName in pairs(files) do
  1246.         term.write(sep .. fileName)
  1247.         sep = ", "
  1248.       end
  1249.     else
  1250.       out.write(files)
  1251.     end
  1252.   end
  1253.  
  1254.   out.setBackgroundColor(objectColors.background)
  1255.   out.setTextColor(objectColors.text)
  1256. end
  1257.  
  1258. objects.FileSelector.click = function(self, x, y)
  1259. error("Not yet implemented")
  1260. -- TODO
  1261.   local finished = false
  1262.   local path = "/"
  1263.   local list = {}
  1264.  
  1265.   while not finished do
  1266.     clearScreen()
  1267.     out.setCursorPos(2, 1)
  1268.     out.write("Path: " .. path)
  1269.    
  1270.     list = getFileList(path)
  1271.    
  1272.     out.setTextColor(objectColors.FileSelector.text)
  1273.   end
  1274. end
  1275.  
  1276. -- >>> Containers
  1277.  
  1278. objects.Container = {}
  1279.  
  1280. objects.Container.getNextFreeKey = function(self)
  1281.   local nextKey = 1
  1282.  
  1283.   while self.children[nextKey] ~= nil do
  1284.     nextKey = nextKey + 1
  1285.   end
  1286.  
  1287.   return nextKey
  1288. end
  1289.  
  1290. -- Returns the area of the container which stores
  1291. -- its children.
  1292. objects.Container.getContentArea = function(self)
  1293.   assert(self)
  1294.   local objType = self.objType
  1295.  
  1296.   if (objects.Container[objType].getContentArea) then
  1297.     return objects.Container[objType].getContentArea(self)
  1298.   else
  1299.     local left = self.x + 1
  1300.     local top = self.y + 1
  1301.     local right = self.x + self.width - 2
  1302.     local bottom = self.y + self.height - 2
  1303.     return left, top, right, bottom
  1304.   end
  1305. end
  1306.  
  1307. -- Determines whether the container itself or its
  1308. -- content area is at the given position.
  1309. objects.Container.contentAreaClicked = function(self, x, y)
  1310.   local left, top, right, bottom = objects.Container.getContentArea(self)    
  1311.  
  1312.   return (x >= left and x <= right and y >= top and y <= bottom)
  1313. end
  1314.  
  1315. -- Draws the buffer and trims it before if
  1316. -- necessary.
  1317. objects.Container.drawBuffer = function(self, buffer, path, x, y, absoluteX, absoluteY, nestLevel)
  1318.   if (self.objType ~= "Window") then
  1319.     local containerLeft, containerTop, containerRight, containerBottom = 1, 1, self.width - 1, self.height - 1
  1320.     local objLeft, objTop, objRight, objBottom = x, y, x + buffer.width - 1, y + buffer.height - 1
  1321.     if (objLeft > self.width or
  1322.         objTop > self.height or
  1323.         objRight < 0 or
  1324.         objBottom < 0) then
  1325.       return
  1326.     elseif (objLeft < containerLeft or objTop < containerTop or objRight > containerRight or objBottom > containerBottom) then
  1327.       local trimLeft = (containerLeft - objLeft) > 0 and containerLeft - objLeft or 0
  1328.       local trimTop = (containerTop - objTop) > 0 and containerTop - objTop or 0
  1329.       local trimRight = (objRight - containerRight + 1) > 0 and objRight - containerRight + 1 or 0
  1330.       local trimBottom = (objBottom - containerBottom + 1) > 0 and objBottom - containerBottom + 1 or 0
  1331.       buffer = buffer:trim(trimLeft, trimTop, trimRight, trimBottom)
  1332.      
  1333.       if (trimLeft > 0) then
  1334.         x = x + trimLeft
  1335.         absoluteX = absoluteX + trimLeft
  1336.       end
  1337.       if (trimTop > 0) then
  1338.         y = y + trimTop
  1339.         absoluteY = absoluteY + trimTop
  1340.       end
  1341.     end
  1342.   end
  1343.  
  1344.   if (nestLevel < #path - 1) then
  1345.     local container = Path.getContainerAt(path, nestLevel + 1)
  1346.     local relX, relY = objects.Container.getRelativePos(container, x, y)
  1347.     objects.Container.drawBuffer(container, buffer, path, relX, relY, absoluteX, absoluteY, nestLevel + 1)
  1348.   else
  1349.     buffer:draw(absoluteX, absoluteY)
  1350.   end
  1351. end
  1352.  
  1353. objects.Container.getRelativePos = function(self, x, y)
  1354.   local left, top, right, bottom = objects.Container.getContentArea(self)
  1355.  
  1356.   retX = x - left + 1
  1357.   retY = y - top + 1
  1358.  
  1359.   return retX, retY
  1360. end
  1361.  
  1362. objects.Container.getParentsRelativePos = function(self, x, y)
  1363.   local left, top, right, bottom = objects.Container.getContentArea(self)
  1364.  
  1365.   return x + left - 1, y + top - 1
  1366. end
  1367.  
  1368. objects.Container.new = function(self, maxWidth, maxHeight)
  1369.   local defaultWidth, defaultHeight = size.Container.width, size.Container.height
  1370.  
  1371.   self.isContainer = true
  1372.   self.canScale = true
  1373.   self.children = {}
  1374.   self.width = (maxWidth < defaultWidth) and maxWidth or defaultWidth
  1375.   self.height = (maxHeight < defaultHeight) and maxHeight or defaultHeight
  1376.  
  1377.   if (objects.Container[self.objType].new) then
  1378.     objects.Container[objectType].new(object)
  1379.   end
  1380. end
  1381.  
  1382. objects.Container.get = function(self)
  1383.   local left, top, right, bottom = objects.Container.getContentArea(self)
  1384.   local width, height = right - left + 1, bottom - top + 1
  1385.   local buffer = Buffer:new()
  1386.   buffer:init(width, height, {})
  1387.  
  1388.   for objectID, object in pairs(self.children) do
  1389.     local objectBuffer = objects.get(object)
  1390.     buffer:addBuffer(object.x, object.y, objectBuffer)
  1391.   end
  1392.  
  1393.   return objects.Container[self.objType].get(self, buffer)
  1394. end
  1395.  
  1396. objects.Container.move = function(self, addX, addY)
  1397.   self.absoluteX = self.absoluteX + addX
  1398.   self.absoluteY = self.absoluteY + addY
  1399.  
  1400.   for _, child in pairs(self.children) do
  1401.     if child.isContainer then
  1402.       objects.Container.move(child, addX, addY)
  1403.     else
  1404.       child.absoluteX = child.absoluteX + addX
  1405.       child.absoluteY = child.absoluteY + addY
  1406.     end
  1407.   end
  1408. end
  1409.  
  1410. -- >> Window
  1411.  
  1412. objects.Container.Window = {}
  1413. objects.Container.Window.new = function(windowName)
  1414.   local object = {}
  1415.   object.objType = "Window"
  1416.   object.parent = "mainWindow"
  1417.   object.children = {}
  1418.   object.width, object.height = maxX, maxY
  1419.  
  1420.   return object
  1421. end
  1422.  
  1423. objects.Container.Window.create = function(windowName)
  1424.   local object = objects.Container.Window.new(windowName)
  1425.   windows.children[windowName] = object
  1426. end
  1427.  
  1428. objects.Container.Window.get = function(self, contentBuffer)
  1429.   return contentBuffer
  1430. end
  1431.  
  1432. objects.Container.Window.getContentArea = function(self)
  1433.   return 1, 1, maxX, maxY
  1434. end
  1435.  
  1436. -- >> Panel
  1437.  
  1438. objects.Container.Panel = {}
  1439. objects.Container.Panel.get = function(self, contentBuffer)
  1440.   local buffer = Buffer:new()
  1441.   buffer:init(self.width, self.height, self.path, objectColors.Container.Panel.border)
  1442.   buffer:addBuffer(2, 2, contentBuffer)
  1443.  
  1444.   return buffer
  1445. end
  1446.  
  1447. -- >>> Objects that can't be added by the user.
  1448.  
  1449. -- >> Line
  1450.  
  1451. objects.Line = {}
  1452. objects.Line.get = function(orientation, length, color)
  1453.   if (orientation ~= "horizontal" and orientation ~= "vertical") then
  1454.     orientation = getOrientation(orientation) or error("Orientation " .. orientation .. " is invalid!")
  1455.   end
  1456.  
  1457.   assert(length)
  1458.   assert(color)
  1459.  
  1460.   local width, height = (orientation == "horizontal") and length, 1 or 1, length
  1461.   local buffer = Buffer:new()
  1462.   buffer:init(width, height, nil, color)
  1463.  
  1464.   return buffer
  1465. end
  1466.  
  1467. -- >> Editor objects
  1468. objects.Selector = {}
  1469. objects.Selector.draw = function(x, y, elements)
  1470.   width = getLongestString(elements) + 2
  1471.   height = #elements + 2 -- Elements + up and down
  1472.   elementCount = #elements
  1473.   displayCount = elementCount
  1474.  
  1475.   enoughXSpace = true
  1476.   -- determine where the selector should actually be displayed
  1477.   if (width > maxX) then -- Not enough monitors horizontally?
  1478.     x = 1
  1479.     enoughXSpace = false
  1480.   elseif (maxX - x < width) then -- Not enough space to the right.
  1481.     if (x >= width) then -- Let's see if there is space to the left.
  1482.       x = x - width
  1483.     else -- No space? Check where you've got more space.
  1484.       if (maxX / 2) > x then -- More space to the left.
  1485.         x = maxX - width + 1
  1486.         enoughXSpace = false
  1487.       else -- More space to the right
  1488.         x = 1
  1489.         enoughXSpace = false
  1490.       end
  1491.     end
  1492.   else -- Enough space to the right.
  1493.     x = x + 1
  1494.   end
  1495.  
  1496.   if (height > maxY - y) then -- Not enough space from y to bottom.
  1497.     if ((maxY / 2) > y) then -- More space below y.
  1498.       if enoughXSpace then
  1499.         if (maxY < height) then -- Too big for the whole screen.
  1500.           y = 1
  1501.           displayCount = maxY - 2
  1502.         else -- Enough space next to x and not too high.
  1503.           y = maxY - height
  1504.         end
  1505.       else -- Can't display it next to the selected point.
  1506.         y = y + 1
  1507.         displayCount = maxY - y - 1
  1508.       end
  1509.     else -- More space above y.
  1510.       if enoughXSpace then
  1511.         if (y < height) then -- Not enough space from top to y.
  1512.           if (maxY < height) then -- Too big for the whole screen.
  1513.             y = 1
  1514.             displayCount = maxY - 2
  1515.           else -- Enough space next to x and not too high.
  1516.             y = 1
  1517.           end
  1518.         else -- Enough space from top to y.
  1519.           y = y - height + 1
  1520.         end
  1521.       else
  1522.         if (y < height) then -- Not enough space from top to y.
  1523.           if (maxY < height) then -- Too big for the whole screen.
  1524.             y = 1
  1525.             displayCount = maxY - 2
  1526.           else -- Not enough space next to x but not too high.
  1527.             y = 1
  1528.             displayCount = y - 4
  1529.           end
  1530.         else -- Enough space from top to y.
  1531.           y = y - height
  1532.         end
  1533.       end
  1534.     end
  1535.   end
  1536.  
  1537.   out.setBackgroundColor(objectColors.background)
  1538.  
  1539.   -- Read the user input.
  1540.   scroll = 1
  1541.   right = x + width - 1
  1542.   bottom = y + displayCount + 1
  1543.  
  1544.   finished = false
  1545.   while not finished do
  1546.     -- Display the actual selector.
  1547.     drawBox(x, y, width, height, objectColors.List.default)
  1548.    
  1549.     out.setBackgroundColor(objectColors["List"].default)
  1550.     middle = math.floor(width / 2)
  1551.     out.setCursorPos(x + middle, y)
  1552.     out.write("^")
  1553.     out.setCursorPos(x + middle, bottom)
  1554.     out.write("V")
  1555.    
  1556.     for i = 1, displayCount do
  1557.       out.setCursorPos(x, y + i)
  1558.       out.write(" " .. elements[i + scroll - 1] .. " ")
  1559.     end
  1560.     out.setBackgroundColor(objectColors.background)
  1561.    
  1562.     touchX, touchY, mouseButton = getCursorInput()
  1563.    
  1564.     if (touchX < x or touchX > right or touchY < y or touchY > bottom) then
  1565.       selectedItem = nil
  1566.       result = false
  1567.       finished = true
  1568.     else -- User touched the selector.
  1569.       if (touchY == y) then -- up
  1570.         if (scroll > 1) then -- Check if it makes sense to scroll up.
  1571.           scroll = scroll - 1
  1572.         end
  1573.       elseif (touchY == bottom) then -- down
  1574.         if (displayCount < elementCount) then
  1575.           if (scroll <= elementCount - displayCount) then
  1576.             scroll = scroll + 1
  1577.           end
  1578.         end
  1579.       else
  1580.         selectedItem = elements[touchY - y + scroll - 1]
  1581.         result = true
  1582.         finished = true
  1583.       end
  1584.     end
  1585.   end
  1586.  
  1587.   drawWindow(currentWindow)
  1588.   return result
  1589. end
  1590.  
  1591. -- >> API Functions
  1592.  
  1593. -- API function: Sets the value of all variables
  1594. -- with the given ID.
  1595. function setVariableValue(variableID, newVar)
  1596.   variableValues[variableID] = newVar
  1597. end
  1598.  
  1599. -- API function: Sets the value of all progressBars
  1600. -- with the given ID.
  1601. function setProgressBarValue(objID, newVar)
  1602.   progressBarValues[objID] = newVar
  1603. end
  1604.  
  1605. -- >> User Input Functions
  1606.  
  1607. -- Gets any input of the user
  1608. -- (not from the environment)
  1609. function getAnyInput()
  1610.   local finished = false
  1611.   local event = {}
  1612.  
  1613.   while not finished do
  1614.     finished = true
  1615.     os.sleep(0)
  1616.    
  1617.     input = {os.pullEvent()}
  1618.     event.eventType = input[1]
  1619.    
  1620.     if (event.eventType == "monitor_touch" and not outIsTerm) then
  1621.       event.eventType = "mouse"
  1622.       event.x = input[3]
  1623.       event.y = input[4]
  1624.       event.mouseButton = 1
  1625.     elseif (event.eventType == "mouse_click" and outIsTerm) then
  1626.       event.eventType = "mouse"
  1627.       event.x = input[3]
  1628.       event.y = input[4]
  1629.       event.mouseButton = input[2]
  1630.     elseif (event.eventType == "key") then
  1631.       event.key = input[2]
  1632.     else
  1633.       finished = false
  1634.     end
  1635.   end
  1636.  
  1637.   return event
  1638. end
  1639.  
  1640. -- Returns where the user clicked and which button
  1641. -- he pressed (always 1 if it's a monitor).
  1642. function getCursorInput()
  1643.   local finished = false
  1644.  
  1645.   while not finished do
  1646.     event, param, x, y = os.pullEvent()
  1647.    
  1648.     if (event == "monitor_touch" and not outIsTerm) then
  1649.       mouseButton = 1
  1650.       finished = true
  1651.     elseif (event == "mouse_click" and outIsTerm) then
  1652.       mouseButton = param
  1653.       finished = true
  1654.     end
  1655.   end
  1656.  
  1657.   return x, y, mouseButton
  1658. end
  1659.  
  1660. -- Waits until any key gets pressed.
  1661. function getKeyInput()
  1662.   os.pullEvent("key")
  1663. end
  1664.  
  1665. function readUserInput(message, isPassword)
  1666.   if not outIsTerm then
  1667.     print(message)
  1668.   end
  1669.    
  1670.   if isPassword  then
  1671.     ret = read("*")
  1672.   else
  1673.     ret = read()
  1674.   end
  1675.  
  1676.   return ret
  1677. end
  1678.  
  1679. -- >> Display Functions
  1680.  
  1681. -- Has to be used instead of paintutils.drawpixel
  1682. function drawPixel(x, y, color)
  1683.   out.setCursorPos(x, y)
  1684.   out.setBackgroundColor(color)
  1685.   out.write(" ")
  1686. end
  1687.  
  1688. function drawBox(x, y, width, height, color)
  1689.   out.setBackgroundColor(color)
  1690.  
  1691.   for row = 1, height do
  1692.     out.setCursorPos(x, y + row - 1)
  1693.     out.write(string.rep(" ", width))
  1694.   end
  1695. end
  1696.  
  1697. -- Displays the text with red background colour.
  1698. function drawSimpleButton(x, y, text)
  1699.   out.setCursorPos(x, y)
  1700.   out.setBackgroundColor(objectColors.Button.default)
  1701.   out.write(text)
  1702.   out.setBackgroundColor(objectColors.background)
  1703. end
  1704.  
  1705. -- Displays the default buttons.
  1706. function drawDefaultButtons()
  1707.   local window = getCurrentWindow()
  1708.   local button
  1709.  
  1710.   if (window.showRefreshButton) then
  1711.     button = defaultButtons.refresh
  1712.     drawSimpleButton(button.left, button.top, button.text) -- Refresh
  1713.   end
  1714.  
  1715.   if (window.showBackButton and currentWindow ~= "mainWindow") then
  1716.     button = defaultButtons.back
  1717.     drawSimpleButton(button.left, button.top, button.text) -- Back
  1718.   end
  1719.  
  1720.   button = defaultButtons.quit
  1721.   drawSimpleButton(button.left, button.top, button.text) -- Quit
  1722.  
  1723.   button = defaultButtons.options
  1724.   if (button.required()) then
  1725.     drawSimpleButton(button.left, button.top, button.text) -- Options
  1726.   end
  1727. end
  1728.  
  1729. -- Loads the values of all variables and progressBars
  1730. -- of the current window.
  1731. function loadObjects()
  1732.   local window = getCurrentWindow()
  1733.   for objectID, object in pairs(window.children) do
  1734.     local objectType = object.objType
  1735.    
  1736.     if (objectType == "Variable" or objectType == "ProgressBar") then
  1737.       local x = object.absoluteX
  1738.       local y = object.absoluteY
  1739.       local value = nil
  1740.      
  1741.       if (objectType == "Variable") then
  1742.         value = getVariableValue(object)
  1743.       elseif (objectType == "ProgressBar") then
  1744.         value = getProgressBarValue(object)
  1745.       end
  1746.      
  1747.       objects.draw(object, value)
  1748.     end
  1749.   end
  1750. end
  1751.  
  1752. -- Displays all objects of the window with the
  1753. -- ID windowID on the screen and changes the
  1754. -- variable "currentWindow".
  1755. function drawWindow(windowID)
  1756.   clearScreen()
  1757.  
  1758.   if windowID then
  1759.     currentWindow = windowID
  1760.   else
  1761.     windowID = currentWindow
  1762.   end
  1763.  
  1764.   local windowObject = getCurrentWindow()
  1765.  
  1766.   windowBuffer = objects.Container.get(windowObject)
  1767.   windowBuffer:draw()
  1768.  
  1769.   if autoLoadObjects then
  1770.     loadObjects()
  1771.   end
  1772.  
  1773.   text.refresh = editMode and "Options" or "Refresh"
  1774.   drawDefaultButtons()
  1775. end
  1776.  
  1777. -- >> Input Processing
  1778.  
  1779. function pullEvent(requestedEvent)
  1780.   if not eventTypeExists(requestedEvent) then
  1781.     clearScreen()
  1782.     print("Event type " .. tostring(requestedEvent) .. " is invalid!")
  1783.     print()
  1784.     print("Available event types:")
  1785.    
  1786.     for _, event in pairs(eventTypes) do
  1787.       print("  " .. event)
  1788.     end
  1789.    
  1790.     error()
  1791.   end
  1792.  
  1793.   local finished = false
  1794.   local event, params
  1795.  
  1796.   while not finished do
  1797.     event, params = getInput()
  1798.    
  1799.     if event then
  1800.       if (requestedEvent ~= nil) then
  1801.         if (requestedEvent == event or event == "quit") then
  1802.           finished = true
  1803.         end
  1804.       else
  1805.         finished = true
  1806.       end
  1807.     end
  1808.   end
  1809.  
  1810.   return event, unpack(params)
  1811. end
  1812.  
  1813. function getInput()
  1814.   local finished = false
  1815.   local event, params
  1816.   local x, y, mouseButton = getCursorInput()
  1817.  
  1818.   if (defaultButtonPressed("quit", x, y)) then
  1819.     quit = true
  1820.   elseif (defaultButtonPressed("refresh", x, y)) then
  1821.     drawWindow()
  1822.     finished = true
  1823.   elseif (defaultButtonPressed("back", x, y)) then
  1824.     if (windows.children[currentWindow].parent ~= nil) then
  1825.       drawWindow(windows.children[currentWindow].parent)
  1826.       finished = true
  1827.     else
  1828.       drawWindow("mainWindow")
  1829.       finished = true
  1830.     end
  1831.   end
  1832.  
  1833.   if finished then
  1834.     return nil
  1835.   elseif quit then
  1836.     return "quit", { "Graffiti" } -- Used for the API
  1837.   end
  1838.  
  1839.   local param
  1840.   local path = windowBuffer.bufferTable[x][y].path
  1841.   if path and #path > 0 then
  1842.     local object = Path.getObject(path)
  1843.     local clickX, clickY = Path.getRelativePos(path, x, y)
  1844.     clickX, clickY = x - object.x + 1, y - object.y + 1
  1845.     event, params = objects.click(object, clickX, clickY)
  1846.   end
  1847.  
  1848.   return event, params
  1849. end
  1850.  
  1851. -- Returns a table with most informations that an AddOn would need.
  1852. function getSystemInfo()
  1853.   systemInfo = {}
  1854.   systemInfo.maxX = maxX
  1855.   systemInfo.maxY = maxY
  1856.   systemInfo.selectedItems = selectedItems
  1857.   systemInfo.userInputs = userInputs
  1858.  
  1859.   return systemInfo
  1860. end
  1861.  
  1862. function callAddOn(object, callType)
  1863.   addOnName = object.addOnName
  1864.   objectID = object.objID
  1865.   x = object.x
  1866.   y = object.y
  1867.   width = object.width
  1868.   height = object.height
  1869.   addOnPath = fs.combine(shell.dir(), addOnName .. addOnExtension)
  1870.  
  1871.   systemInfo = getSystemInfo()
  1872.   systemInfo.x = x
  1873.   systemInfo.y = y
  1874.   systemInfo.width = width
  1875.   systemInfo.height = height
  1876.  
  1877.   if changeButtonColor then
  1878.       objects["Button"].draw(object, objectColors["Button"].active)
  1879.   end
  1880.  
  1881.   shell.run(addOnPath, callType, objectID, textutils.serialize(systemInfo))
  1882.  
  1883.   if changeButtonColor then
  1884.     objects["Button"].draw(button, objectColors["Button"].default)
  1885.   else
  1886.     changeButtonColor = true
  1887.   end
  1888. end
  1889.  
  1890. -- Shows the message on the computer for debugging. Probably my most-used function.
  1891. function debugMessage(message)
  1892.   if outIsTerm then
  1893.     error("Can't display a debug message on a computer!")
  1894.   end
  1895.  
  1896.   print(message)
  1897. end
  1898.  
  1899. -- Calls the "getInput" function until the user presses the quit-button.
  1900. function main()
  1901.   drawWindow("mainWindow")
  1902.  
  1903.   while not quit do
  1904.     getInput()
  1905.   end
  1906. end
  1907.  
  1908. -- Saves the content of the windows-table into the save file
  1909. function saveWindows()
  1910.   saveString = textutils.serialize(windows.children)
  1911.   file = fs.open(saveFileName, "w")
  1912.   file.write(saveString)
  1913.   file.close()
  1914. end
  1915.  
  1916. -- Loads the save file and puts the content into the windows-table
  1917. function loadWindows()
  1918.   if not fs.exists(saveFileName) then
  1919.     return
  1920.   end
  1921.  
  1922.   file = fs.open(saveFileName, "r")
  1923.   loadString = file.readAll()
  1924.   if (loadString ~= nil and loadString ~= "") then
  1925.     windows.children = textutils.unserialize(loadString)
  1926.   end
  1927.   file.close()
  1928. end
  1929.  
  1930. function splitAt(self, delimiter)
  1931.   delimiterPos = string.find(self, delimiter)
  1932.   left = string.sub(self, 1, delimiterPos - 1)
  1933.   right = string.sub(self, delimiterPos + #delimiter)
  1934.  
  1935.   return left, right
  1936. end
  1937.  
  1938. -- >>> Editor
  1939.  
  1940. function generateWindowList()
  1941.   ret = { "mainWindow" }
  1942.   for key, value in pairs(windows.children) do
  1943.     if (key ~= "mainWindow") then
  1944.       table.insert(ret, key)
  1945.     end
  1946.   end
  1947.  
  1948.   return ret
  1949. end
  1950.  
  1951. editorWindows = {
  1952.   children = {
  1953.     mainWindow = {
  1954.       objType = "Window",
  1955.       children = {
  1956.         [1] = { objType="Text", x=2, y=1, text="Mode:", path={1} };
  1957.         [2] = { objType="List", x=2, y=3, elements=editActions, objID="editActionList", isMultiselect=false, canClick=true, path={2} };
  1958.         [3] = { objType="Button", x=2, y=7, width=13, height=3, text="Last Window", funcType="function", param="editLastWindow", canClick=true, canScale=true, path={3} };
  1959.         [4] = { objType="Button", x=17, y=7, width=13, height=3, text="Window List", funcType="function", param="loadWindowList", canClick=true, canScale=true, path={4} };
  1960.       };
  1961.     };
  1962.    
  1963.     windowListWindow = {
  1964.       objType = "Window",
  1965.       children = {
  1966.         [1] = { objType="List", x=2, y=2, elements=windowList, objID="windowList", isMultiselect=false, canClick=true, path={1} };
  1967.         [2] = { objType="Button", x=2, y=maxY-6, width=12, height=1, text="Set parent", funcType="function", param="setParent", canClick=true, canScale=true, path={2} };
  1968.         [3] = { objType="Button", x=2, y=maxY-4, width=8, height=1, text="New", funcType="function", param="newWindow", canClick=true, canScale=true, path={3} };
  1969.         [4] = { objType="Button", x=2, y=maxY-3, width=8, height=1, text="Edit", funcType="function", param="editWindow", canClick=true, canScale=true, path={4} };
  1970.         [5] = { objType="Button", x=2, y=maxY-2, width=8, height=1, text="Delete", funcType="function", param="deleteWindow", canClick=true, canScale=true, path={5} };
  1971.       };
  1972.     };
  1973.   };
  1974. }
  1975.  
  1976. -- Used to give a List-object an array of all windows
  1977. function editorFunctions.loadWindowList()
  1978.   windowList = generateWindowList()
  1979.   editorWindows.children.windowListWindow.children[1].elements = windowList
  1980.   changeButtonColor = false
  1981.   drawWindow("windowListWindow")
  1982. end
  1983.  
  1984. function editorFunctions.editLastWindow()
  1985.   if (lastWindow == nil) then
  1986.     lastWindow = "mainWindow"
  1987.   end
  1988.  
  1989.   showEditorOptions = false
  1990.   drawWindow(lastWindow)
  1991.   changeButtonColor = false
  1992. end
  1993.  
  1994. -- Let's the user define the parent-attribute of the current window.
  1995. function editorFunctions.setParent()
  1996.   if (selectedItems.windowList == nil) then
  1997.     return
  1998.   end
  1999.  
  2000.   local list = editorWindows.children.windowListWindow.children[1]
  2001.   local selected = objects.List.getFirstSelectedKey(list)
  2002.  
  2003.   for i = 1, list.height do
  2004.     if (i ~= selected) then
  2005.       if (windowList[i] == windows.children[windowList[selected]].parent) then
  2006.         drawPixel(1, i + 1, colors.yellow)
  2007.       else
  2008.         drawPixel(1, i + 1, colors.lime)
  2009.       end
  2010.     end
  2011.   end
  2012.  
  2013.   local x, y, mouseButton = getCursorInput()
  2014.   local selectedParent = y - list.y + 1
  2015.  
  2016.   if (selectedParent >= 1 and selectedParent <= list.height) then -- Clicked inside the list.
  2017.     if (selectedParent ~= selected) then -- Selected parentWindow is not selected window.
  2018.       windows.children[windowList[selected]].parent = windowList[selectedParent]
  2019.     end
  2020.   end
  2021.  
  2022.   for i = 1, list.height do
  2023.     drawPixel(1, i + list.y - 1, colors.black)
  2024.   end
  2025. end
  2026.  
  2027. -- Creates a new window. The user has to enter the window name in the computer.
  2028. function editorFunctions.newWindow()
  2029.   clearScreen()
  2030.  
  2031.   if not outIsTerm then
  2032.     out.setCursorPos(2, 2)
  2033.     out.write("Enter a window-name.")
  2034.   end
  2035.  
  2036.   out.setCursorPos(1, 1)
  2037.  
  2038.   message = "Pleas enter the name of the new window or nothing to cancel."
  2039.   userInput = readUserInput(message, false)
  2040.  
  2041.   while (userInput ~= nil and userInput ~= "" and windows.children[userInput] ~= nil) do
  2042.     message = "There is already a window with that name!"
  2043.     userInput = readUserInput(message, false)
  2044.   end
  2045.  
  2046.   if (userInput ~= nil and userInput ~= "") then
  2047.     objects.Container.Window.create(userInput)
  2048.     showEditorOptions = false
  2049.     drawWindow(userInput)
  2050.     lastWindow = userInput
  2051.     changeButtonColor = false
  2052.   end
  2053. end
  2054.  
  2055. -- Edits the window that has been selected in the "windowList"-list.
  2056. function editorFunctions.editWindow()
  2057.   if selectedItems.windowList then
  2058.     local list = editorWindows.children.windowListWindow.children[1]
  2059.     local key = objects.List.getFirstSelectedKey(list)
  2060.     key = key or 1
  2061.    
  2062.     showEditorOptions = false
  2063.     lastWindow = windowList[key]
  2064.     drawWindow(lastWindow)
  2065.     changeButtonColor = false
  2066.   end
  2067. end
  2068.  
  2069. -- Deletes the window that has been selected in the "windowList"-list.
  2070. function editorFunctions.deleteWindow()
  2071.   local list = editorWindows.children.windowListWindow.children[1]
  2072.   local key = objects.List.getFirstSelectedKey(list)
  2073.  
  2074.   if (key and windowList[key] ~= "mainWindow") then
  2075.     windows.children[windowList[key]] = nil
  2076.     showEditorOptions = true
  2077.     editorFunctions.loadWindowList()
  2078.   end
  2079. end
  2080.  
  2081. -- Shows lines marking the top left part of an
  2082. -- object as well as well as pixels displaying
  2083. -- the alignment of an object.
  2084. function drawAlignmentLines(object, left, top, right, bottom)
  2085.   local color = objectColors.Editor.marker
  2086.   local moveX, moveY = objects.getMovePos(object)
  2087.  
  2088.   -- Draw the lines.
  2089.   objects["Line"].draw(left - 1, moveY, "left", left - 2, color) -- left
  2090.   objects["Line"].draw(moveX, top -1, "up", top - 2, color) -- up
  2091.   objects["Line"].draw(right + 1, moveY, "right", maxX - (right + 1), color) -- right
  2092.   objects["Line"].draw(moveX, bottom + 1, "down", maxY - (bottom + 1), color) -- down
  2093.  
  2094.   -- Display the alignment-pixels.
  2095.   horizontalAlignment = object.horizontalAlignment
  2096.   verticalAlignment = object.verticalAlignment
  2097.  
  2098.   if (horizontalAlignment == "left" or horizontalAlignment == "stretch") then -- left
  2099.     drawPixel(1, moveY, objectColors["Editor"].alignmentTrue)
  2100.   else
  2101.     drawPixel(1, moveY, objectColors["Editor"].alignmentFalse)
  2102.   end
  2103.  
  2104.   if (horizontalAlignment == "right" or horizontalAlignment == "stretch") then -- right
  2105.     drawPixel(maxX, moveY, objectColors["Editor"].alignmentTrue)
  2106.   else
  2107.     drawPixel(maxX, moveY, objectColors["Editor"].alignmentFalse)
  2108.   end
  2109.  
  2110.   if (verticalAlignment == "top" or verticalAlignment == "stretch") then -- top
  2111.     drawPixel(moveX, 1, objectColors["Editor"].alignmentTrue)
  2112.   else
  2113.     drawPixel(moveX, 1, objectColors["Editor"].alignmentFalse)
  2114.   end
  2115.  
  2116.   if (verticalAlignment == "bottom" or verticalAlignment == "stretch") then -- bottom
  2117.     drawPixel(moveX, maxY, objectColors["Editor"].alignmentTrue)
  2118.   else
  2119.     drawPixel(moveX, maxY, objectColors["Editor"].alignmentFalse)
  2120.   end
  2121.  
  2122.   out.setBackgroundColor(objectColors.background)
  2123. end
  2124.  
  2125. -- Returns the values of horizontalAlignment and
  2126. -- verticalAlignment depending which sides are set
  2127. -- to true.
  2128. function getAlignment(left, top, right, bottom)
  2129.   local retHorizontal, retVertical = "left", "top"
  2130.  
  2131.   if right then
  2132.     if left then
  2133.       retHorizontal = "stretch"
  2134.     else
  2135.       retHorizontal = "right"
  2136.     end
  2137.   else
  2138.     retHorizontal = "left"
  2139.   end
  2140.  
  2141.   if bottom then
  2142.     if top then
  2143.       retVertical = "stretch"
  2144.     else
  2145.       retVertical = "bottom"
  2146.     end
  2147.   else
  2148.     retVertical = "top"
  2149.   end
  2150.  
  2151.   return retHorizontal, retVertical
  2152. end
  2153.  
  2154. -- Let's the user delete an object or change its attributes depending on the current edit-mode.
  2155. function editObject(object)
  2156.   assert(object)
  2157.  
  2158.   local objType = object.objType
  2159.   local left, top, right, bottom = objects.getDimensions(object)
  2160.   local actionsList = editorWindows.children.mainWindow.children[2]
  2161.   local action = editActions[objects.List.getFirstSelectedKey(actionsList)]
  2162.  
  2163.   if (action == "Delete") then
  2164.     objects.remove(object)
  2165.   elseif (action == "Attributes") then
  2166.     local objAttr = {  }
  2167.     local includedAttributes = {
  2168.       text = true,
  2169.       param = true,
  2170.       objID = true,
  2171.       message = true,
  2172.       elements = true,
  2173.       message = true,
  2174.       funcType = true,
  2175.       isPassword = true,
  2176.       isMultiselect = true,
  2177.     }
  2178.    
  2179.     index = 1
  2180.     for key, value in pairs(object) do
  2181.       if (includedAttributes[key]) then
  2182.         table.insert(objAttr, index, key)
  2183.         index = index + 1
  2184.       end
  2185.     end
  2186.    
  2187.     out.clear()
  2188.    
  2189.     local yPos = 2
  2190.     top = yPos
  2191.     for attrKey, attrValue in ipairs(objAttr) do
  2192.       out.setCursorPos(2, yPos)
  2193.       out.write(attrValue .. ": ")
  2194.       out.write(object[attrValue])
  2195.       yPos = yPos + 1
  2196.     end
  2197.     out.setCursorPos(2, yPos + 1)
  2198.     out.setBackgroundColor(colors.red)
  2199.     out.write(text.done)
  2200.     out.setBackgroundColor(objectColors.background)
  2201.    
  2202.     bottom = yPos - 1
  2203.     finished = false
  2204.     while not finished do
  2205.       local x, y, mouseButton = getCursorInput()
  2206.      
  2207.       if y >= top and y <= bottom then
  2208.         local selectedAttr = objAttr[y - 1]
  2209.         if not outIsTerm then
  2210.           drawPixel(1, y, colors.yellow)
  2211.         end
  2212.        
  2213.         if (selectedAttr == "param" or
  2214.             selectedAttr == "objID" or
  2215.             selectedAttr == "message" or
  2216.             selectedAttr == "elements" or
  2217.             selectedAttr == "message") then
  2218.          
  2219.           if outIsTerm then
  2220.             out.setCursorPos(1, y)
  2221.             out.clearLine(y)
  2222.             out.setCursorPos(2, y)
  2223.             out.write(selectedAttr .. ": ")
  2224.           end
  2225.          
  2226.           userInput = readUserInput("Please enter a value for the " .. selectedAttr .. ".", false)
  2227.           if (userInput ~= nil) then
  2228.             object[selectedAttr] = userInput
  2229.           end
  2230.         elseif (selectedAttr == "text") then
  2231.           if outIsTerm then
  2232.             out.setCursorPos(1, y)
  2233.             out.clearLine(y)
  2234.             out.setCursorPos(2, y)
  2235.             out.write(selectedAttr .. ": ")
  2236.           end
  2237.          
  2238.           userInput = readUserInput("Please enter a value for the " .. selectedAttr .. ".", false)
  2239.           if (userInput ~= nil) then
  2240.             object[selectedAttr] = userInput
  2241.             object.width = #userInput
  2242.           end
  2243.         elseif (selectedAttr == "funcType") then -- Button attribute
  2244.           if (object.funcType == "switch") then
  2245.             object[selectedAttr] = "function"
  2246.           else
  2247.             object[selectedAttr] = "switch"
  2248.           end
  2249.         elseif (selectedAttr == "isPassword" or selectedAttr == "isMultiselect") then
  2250.           if (object[selectedAttr]) then
  2251.             object[selectedAttr] = false
  2252.           else
  2253.             object[selectedAttr] = true
  2254.           end
  2255.         end
  2256.         drawPixel(1, y, colors.black)
  2257.         if (not finished and selectedAttr ~= nil) then
  2258.           out.setCursorPos(2, y) -- I don't know if that's neccessary...
  2259.           for i = 2, maxX do
  2260.             out.write(" ")
  2261.           end
  2262.           out.setCursorPos(2, y)
  2263.           out.write(selectedAttr .. ": ")
  2264.           out.write(object[selectedAttr])
  2265.         end
  2266.       elseif (y == yPos + 1 and x >= 2 and x <= 1 + string.len(text.done)) then
  2267.         finished = true
  2268.       end
  2269.     end
  2270.   else -- Design mode
  2271.     local moveX, moveY = objects.getMovePos(object)
  2272.     local scaleX, scaleY
  2273.    
  2274.     if object.canScale then
  2275.       scaleX, scaleY = objects.getScalePos(object)
  2276.       drawPixel(scaleX, scaleY, objectColors.Editor.scale)
  2277.     end
  2278.    
  2279.     drawPixel(moveX, moveY, objectColors.Editor.move)
  2280.     out.setBackgroundColor(objectColors.background)
  2281.    
  2282.     local x, y, mouseButton = getCursorInput()
  2283.     local relX, relY = Path.getRelativePos(object.path, x, y)
  2284.    
  2285.     if (relX >= left and relX <= right and relY >= top and relY <= bottom) then -- clicked inside the object
  2286.       if (x == moveX and y == moveY) then -- move object
  2287.         drawPixel(moveX, moveY, objectColors.Editor.active)
  2288.         x, y, mouseButton = getCursorInput()
  2289.         addX = x - moveX
  2290.         addY = y - moveY
  2291.         objects.move(object, addX, addY)
  2292.       elseif (object.canScale and x == scaleX and y == scaleY) then -- scale object
  2293.         drawPixel(scaleX, scaleY, objectColors.Editor.active)
  2294.         out.setBackgroundColor(objectColors.background)
  2295.         local x, y, mouseButton = getCursorInput()
  2296.         local relX, relY = Path.getRelativePos(object.path, x, y)
  2297.        
  2298.         objects.scale(object, relX, relY)
  2299.       else -- clicked something else inside the object (no idea what I could use this for)
  2300.        
  2301.       end
  2302.     end
  2303.   end
  2304.  
  2305.   out.setBackgroundColor(objectColors.background)
  2306.   drawWindow(currentWindow)
  2307. end
  2308.  
  2309. function markVariables(container)
  2310.   assert(container)
  2311.  
  2312.   for _, object in pairs(container.children) do
  2313.     if (object.isContainer) then
  2314.       markVariables(object)
  2315.     elseif (object.objType == "Variable") then
  2316.       drawPixel(object.absoluteX, object.absoluteY, objectColors.Editor.marker)
  2317.       out.setBackgroundColor(objectColors.background)
  2318.     end
  2319.   end
  2320. end
  2321.  
  2322. function markDefaultButtons()
  2323.   local window = getCurrentWindow()
  2324.  
  2325.   -- refresh button
  2326.   local refresh = defaultButtons.refresh
  2327.   out.setCursorPos(refresh.left, refresh.top)
  2328.   if (window.showRefreshButton) then
  2329.     out.setBackgroundColor(objectColors.Button.default)
  2330.     out.write(refresh.text)
  2331.   else
  2332.     out.setBackgroundColor(objectColors.Editor.marker)
  2333.     out.write(string.rep(" ", #refresh.text))
  2334.   end
  2335.  
  2336.   -- back button
  2337.   if (currentWindow ~= "mainWindow") then
  2338.     local back = defaultButtons.back
  2339.     out.setCursorPos(back.left, back.top)
  2340.     if (window.showBackButton) then
  2341.       out.setBackgroundColor(objectColors.Button.default)
  2342.       out.write(back.text)
  2343.     else
  2344.       out.setBackgroundColor(objectColors.Editor.marker)
  2345.       out.write(string.rep(" ", #back.text))
  2346.     end
  2347.   end
  2348.  
  2349.   out.setBackgroundColor(objectColors.background)
  2350. end
  2351.  
  2352. function getEditorInput()
  2353.   local event
  2354.   local x, y, mouseButton
  2355.  
  2356.   if not showEditorOptions then
  2357.     markVariables(getCurrentWindow())
  2358.     markDefaultButtons()
  2359.     event = getAnyInput()
  2360.    
  2361.     if (event.eventType == "mouse") then
  2362.       x, y, mouseButton = event.x, event.y, event.mouseButton
  2363.     end
  2364.   end
  2365.  
  2366.   if (not showEditorOptions and event.eventType == "key") then
  2367.     callShortcut(event.key)
  2368.   elseif (showEditorOptions or defaultButtonPressed("options", x, y)) then
  2369.     showEditorOptions = true
  2370.     drawWindow("mainWindow")
  2371.     while showEditorOptions and not quit do
  2372.       getInput()
  2373.     end
  2374.   elseif (defaultButtonPressed("quit", x, y)) then
  2375.     quit = true
  2376.   elseif (defaultButtonPressed("refresh", x, y)) then
  2377.     windows.children[currentWindow].showRefreshButton = not windows.children[currentWindow].showRefreshButton
  2378.   elseif (defaultButtonPressed("back", x, y)) then
  2379.     windows.children[currentWindow].showBackButton = not windows.children[currentWindow].showBackButton
  2380.   else
  2381.     local container = getCurrentWindow()
  2382.     local path = windowBuffer.bufferTable[x][y].path
  2383.    
  2384.     if (path == nil or #path == 0) then -- No object touched. Draw selector for new object.
  2385.       drawPixel(x, y, objectColors.Editor.new)
  2386.       if (objects.Selector.draw(x, y, objectTypes)) then -- something has been selected
  2387.         objects.create(selectedItem, x, y)
  2388.       end
  2389.     else
  2390.       local object = Path.getObject(path)
  2391.      
  2392.       if (mouseButton == 1) then
  2393.         editObject(object)
  2394.       else
  2395.         if (objects.Selector.draw(x, y, rightClickActions)) then
  2396.           if (selectedItem == "Attributes") then
  2397.             lastItem = selectedItems.editActionList
  2398.             selectedItems.editActionList = { [2] = true }
  2399.             editObject(object)
  2400.             selectedItems.editActionList = lastItem
  2401.           elseif (selectedItem == "Delete") then
  2402.             objects.remove(object)
  2403.             drawWindow()
  2404.           end
  2405.         end
  2406.       end
  2407.     end
  2408.   end
  2409. end
  2410.  
  2411. function windowEditor()
  2412.   editMode = true
  2413.   autoLoadObjects = false
  2414.  
  2415.   showEditorOptions = true
  2416.  
  2417.   while not quit do
  2418.     getEditorInput()
  2419.   end
  2420. end
  2421.  
  2422. -- >>> Screen size adaption
  2423.  
  2424. function round(number)
  2425.   assert(number)
  2426.   comma = number % 1
  2427.   if comma < 0.5 then
  2428.     ret = math.floor(number)
  2429.   else
  2430.     ret = math.ceil(number)
  2431.   end
  2432.  
  2433.   return ret
  2434. end
  2435.  
  2436. function printInfo()
  2437.   print()
  2438.   print(version)
  2439.   print("Author: Encreedem")
  2440.   print()
  2441.   print("Param(s):")
  2442.   print("info - Shows some info about the program... but I guess you know that already.")
  2443.   print("edit - Starts the program in edit-mode.")
  2444.   print()
  2445.   print("Visit the CC-forums or my YouTube channel (LPF1337) for news and help.")
  2446. end
  2447.  
  2448. function testMethod()
  2449.   error("Nothing to test...")
  2450. end
  2451.  
  2452. -- >>> initialization
  2453.  
  2454. -- Tells the user that the monitor or computer
  2455. -- doesn't support colors.
  2456. function showColorWarning()
  2457.   out.clear()
  2458.   out.setCursorPos(2, 2)
  2459.   out.write("This computer/monitor does not support colors!")
  2460.  
  2461.   local state = 0
  2462.   local move = "I don't know this move!"
  2463.   local finished = false
  2464.   while not finished and not quit do
  2465.     out.setCursorPos(1, 4)
  2466.     out.clearLine()
  2467.     out.setCursorPos(2, 4)
  2468.    
  2469.     if (state == 0) then
  2470.       move = "<( \" <) <( \" <) <( \" <)"
  2471.     elseif (state == 1 or state == 3 or state == 5) then
  2472.       move = "  (^\"^)   (^\"^)   (^\"^)"
  2473.     elseif (state == 2) then
  2474.       move = "  (> \" )> (> \" )> (> \" )>"
  2475.     elseif (state == 4) then
  2476.       move = " (> \" )><( \" )><( \" <)"
  2477.     elseif (state == 6) then
  2478.       move = "<( \" <) (>\"<) (> \" )>"
  2479.     elseif (state == 7) then
  2480.       move = " (v''v) (v''v) (v''v)"
  2481.     else
  2482.       error("Unable to show you that you need an advanced computer/monitor in a fancy way!")
  2483.     end
  2484.    
  2485.     out.write(move)
  2486.     state = (state + 1) % 8
  2487.     os.sleep(0.25)
  2488.   end
  2489. end
  2490.  
  2491. -- Checks if the monitor on monitorSide exists and wraps it into "monitor".
  2492. function getOutput()
  2493.   if (monitor == nil and outIsTerm == false) then
  2494.     local monitorFound = false
  2495.     for _, side in pairs(sides) do
  2496.       if (peripheral.getType(side) == "monitor") then
  2497.         monitor = peripheral.wrap(side)
  2498.         monitorFound = true
  2499.         out = monitor
  2500.         outIsTerm = false
  2501.       end
  2502.     end
  2503.    
  2504.     if not monitorFound then
  2505.       out = term
  2506.       outIsTerm = true
  2507.     end
  2508.   elseif outIsTerm then
  2509.     out = term
  2510.   else
  2511.     out = monitor
  2512.   end
  2513. end
  2514.  
  2515. function init()
  2516.   getOutput()
  2517.  
  2518.   maxX, maxY = out.getSize()
  2519.   if (maxX < 16 or maxY < 10) then -- smaller than 2x2
  2520.     print("Screen too small! You need at least 2x2 monitors!")
  2521.     return false
  2522.   elseif not out.isColor() then
  2523.     parallel.waitForAny(showColorWarning, getKeyInput)
  2524.     out.clear()
  2525.     out.setCursorPos(1, 1)
  2526.     return false
  2527.   end
  2528.  
  2529.   isAPI = (shell == nil)
  2530.  
  2531.   initDone = true
  2532.   return true
  2533. end
  2534.  
  2535. function checkArgs()
  2536.   doCall = main
  2537.   arg = args[1]
  2538.  
  2539.   if (arg ~= nil) then
  2540.     if (arg == "edit") then
  2541.       doCall = windowEditor
  2542.     elseif (arg == "info") then
  2543.       doCall = printInfo
  2544.     elseif (arg == "term") then
  2545.       outIsTerm = true
  2546.     elseif (arg == "test") then
  2547.       doCall = testMethod
  2548.     end
  2549.   end
  2550.  
  2551.   doCall()
  2552. end
  2553.  
  2554. if init() then
  2555.   loadWindows()
  2556.   initDefaultButtons()
  2557.  
  2558.   if not isAPI then
  2559.     loadWindows()
  2560.     checkArgs()
  2561.    
  2562.     if editMode then
  2563.       saveWindows()
  2564.     end
  2565.    
  2566.     out.setTextColor(colors.white)
  2567.     out.setBackgroundColor(colors.black)
  2568.     out.clear()
  2569.     out.setCursorPos(1, 1)
  2570.   end
  2571. else
  2572.   error("Graffiti Initialization failed!")
  2573. end
Advertisement
Add Comment
Please, Sign In to add comment