Advertisement
PaymentOption

Desktop Utility for CC 1.46

Nov 1st, 2012
371
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.86 KB | None | 0 0
  1. --[[
  2.         Desktop Shell by PaymentOption
  3.         31 October 2012
  4.  
  5.         This is a simple clickable desktop interface for ComputerCraft 1.46
  6.         by dan200.
  7.  
  8.         Change Log:
  9.                 - 0.1: Initial Release.
  10.                 - 0.2:
  11.                         * Added scrolling for directories with more entries than the screen would permit.
  12.                         * Fixed some bugs with the backgrounds of the lists being abnormally long.
  13.                         * Refactored the way directory and position in the computer is handled.
  14.                 - 0.3: Changed scrolling from click triggered to mouse-wheel triggered.
  15.                 - 0.4: Added a fairly crass way to call programs with arguments using a text field.
  16. --]]
  17.  
  18. --=VARIABLES===========================--
  19. local tScreenBuffer = {} -- This will be all of the text that is written to the screen that shouldn't be removed.
  20. local screenWidth, screenHeight = term.getSize() -- The x and y dimensions of the screen.
  21. local scrollOffset = 1 -- The amount of offsetting done by scrolling.
  22.  
  23. local currentDirectory = nil -- The current directory that we are in. nil = Start menu.
  24.  
  25. local EXIT_KEY = 14 -- This is the key that must be pressed to exit the program.
  26. --=END VARIABLES=======================--
  27.  
  28.  
  29.  
  30. --=SCREEN BUFFER FUNCTIONS===============--
  31. -- Writes text to the screen while also adding an entry to the screen buffer.
  32. -- NOTE: Will reset the text color back to white and the background color
  33. --       back to black after the function completes.
  34. function writeToScreen(text, givenColor, givenBackgroundColor, givenPath)
  35.         -- If the color given was invalid, then set it to white.
  36.         local x, y = term.getCursorPos()
  37.         giveColor = isColorValid(givenColor) or colors.white
  38.         givenBackgroundColor = isColorValid(givenBackgroundColor) or colors.black
  39.  
  40.         table.insert(tScreenBuffer, {text = text, path = givenPath, xPos = x, yPos = y, textColor = givenColor, backgroundColor = givenBackgroundColor})
  41.  
  42.         term.setTextColor(givenColor)
  43.         term.setBackgroundColor(givenBackgroundColor)
  44.         term.write(text)
  45.  
  46.         -- Reset the text color to white and the background color to black.
  47.         term.setTextColor(colors.white)
  48.         term.setBackgroundColor(colors.black)
  49. end
  50.  
  51. -- Empties the screen buffer.
  52. function dumpBuffer()
  53.         tScreenBuffer = {}
  54. end
  55.  
  56. -- Dumps the screen buffer and initializes it with the start option.
  57. function initalizeBuffer()
  58.         dumpBuffer()
  59.         term.setCursorPos(1, screenHeight)
  60.         writeToScreen("START", colors.white, colors.blue)
  61. end
  62.  
  63. -- Redraws all contents of the screen buffer to the screen.
  64. -- NOTE: Will reset the text color back to white and the background color
  65. --       back to black after the function completes.
  66. function redrawBuffer()
  67.         for index, item in pairs(tScreenBuffer) do
  68.                 term.setBackgroundColor(item.backgroundColor)
  69.                 term.setTextColor(item.textColor)
  70.  
  71.                 term.setCursorPos(item.xPos, item.yPos)
  72.                 term.write(item.text)
  73.         end
  74.  
  75.         -- Reset the text color to white and the background color to black.
  76.         term.setBackgroundColor(colors.black)
  77.         term.setTextColor(colors.white)
  78. end
  79.  
  80. -- Uses the screen buffer to draw the contents of a directory like a list.
  81. function drawDirectoryContents(directoryPath, xPos, yPos)
  82.         -- Make sure that the directory given exists.
  83.         if fs.isDir(directoryPath) then
  84.                 -- Get the contents of the directory.
  85.                 local directoryContents = fs.list(directoryPath)
  86.                 -- Append the length of all entries to the length of the longest entry in the table.
  87.                 -- This way when we draw the backgrounds for the items their backgroudns will all be the same length.
  88.                 directoryContents = appendContentsLengths(directoryContents, directoryPath)
  89.  
  90.                 -- Use the 'writeToScreen' function to draw this directory's contents.
  91.                 for index = 1, #directoryContents do
  92.                         term.setCursorPos(xPos, yPos - index + 1)
  93.                         writeToScreen(directoryContents[index].label, colors.white, colors.gray, directoryContents[index].path)
  94.                 end
  95.         end
  96. end
  97.  
  98. -- Uses a table to draw the contents of a directory like a list.
  99. function drawTable(contents, xPos, yPos)
  100.         for index = 1, #contents do
  101.                 term.setCursorPos(xPos, yPos - index + 1)
  102.                 writeToScreen(contents[index].label, colors.white, colors.gray, contents[index].path)
  103.         end
  104. end
  105. --=END SCREEN BUFFER FUNCTIONS===========--
  106.  
  107.  
  108.  
  109. --=COLOR FUNCTIONS=======================--
  110. -- Checks if a given color is valid. If the color is valid,
  111. -- then this function returns aforementioned color; if it's false,
  112. -- then nil is returned.
  113. function isColorValid(givenColor)
  114.         for index, color in pairs(colors) do
  115.                 if color == givenColor then
  116.                         return givenColor
  117.                 end
  118.         end
  119.  
  120.         -- If the loop completed without returning, then the color
  121.         -- given is invalid and therefore this function will return nil.
  122.         return nil
  123. end
  124. --=END COLOR FUNCTIONS===================--
  125.  
  126.  
  127.  
  128. --=TEXT MANIPULATING FUNCTIONS===========--
  129. -- Returns the longest length of a string found in a given table.
  130. function getLongestLength(directoryPath)
  131.         local directoryContents = fs.list(directoryPath) -- The contents of the directory to get the longest label of.
  132.         local longestLength = 0 -- This will be the length of the longest entry in the table.
  133.         for index, item in ipairs(directoryContents) do
  134.                 longestLength = math.max(longestLength, item:len()) -- Add one to accomodate the space for the '>' later.
  135.         end
  136.  
  137.         return longestLength
  138. end
  139.  
  140. -- Appends the length of all entries in a directory table with spaces so their
  141. -- lengths are all equal (the longest entry in the table).
  142. function appendContentsLengths(directoryContents, directoryPath)
  143.         local longestLength = getLongestLength(directoryPath)
  144.  
  145.         -- Append the labels for all of the entries in the directory table using 'longestLength'
  146.         -- so that they are of equal length.
  147.         local appendage = "" -- The text to be added to t=each item.
  148.         for index, item in ipairs(directoryContents) do
  149.                 -- If the current item is a directory, then add a '>' to the end of its label.
  150.                 if fs.isDir(directoryPath .. '/' .. item) then
  151.                         directoryContents[index] = {label = item .. string.rep(' ', longestLength - item:len()) .. '>', path = directoryPath .. '/' .. item}
  152.                 else
  153.                         directoryContents[index] = {label = item .. string.rep(' ', longestLength - item:len()) .. ' ', path = directoryPath .. '/' .. item}
  154.                 end
  155.         end
  156.         -- Return the appended directory contents as a table.
  157.         return directoryContents
  158. end
  159.  
  160. -- From a list of arguments seperated by spacing, this function returns a table
  161. -- with each argument separated by a space. BORROWED FROM: http://lua-users.org/wiki/SplitJoin
  162. -- Compatibility: Lua-5.1
  163. function splitArgs(str, pat)
  164.    local t = {}  -- NOTE: use {n = 0} in Lua-5.0
  165.    local fpat = "(.-)" .. pat
  166.    local last_end = 1
  167.    local s, e, cap = str:find(fpat, 1)
  168.    while s do
  169.       if s ~= 1 or cap ~= "" then
  170.          table.insert(t,cap)
  171.       end
  172.       last_end = e+1
  173.       s, e, cap = str:find(fpat, last_end)
  174.    end
  175.    if last_end <= #str then
  176.       cap = str:sub(last_end)
  177.       table.insert(t, cap)
  178.    end
  179.    return t
  180. end
  181. --=END TEXT MANIPULATING FUNCTIONS=======--
  182.  
  183.  
  184.  
  185. --=SCREEN UTILITY FUNCTIONS==============--
  186. -- Clears the screen of all text and repositions the cursor to the origin of the screen (1, 1).
  187. function clearScreen()
  188.         term.clear()
  189.         term.setCursorPos(1, 1)
  190. end
  191.  
  192. -- Draws the current directory to the screen.
  193. function displayPath()
  194.         -- If the current directory is longer than 25 characters, then truncate it to be drawn on the screen.
  195.         local directory = currentDirectory
  196.         if directory:len() > 25 then
  197.                 directory = directory:sub(1, 25)
  198.         end
  199.  
  200.         -- Set the text color to yellow, the background to black, draw the directory, then reset the colors to white and black.
  201.         term.setTextColor(colors.yellow)
  202.         term.setBackgroundColor(colors.black)
  203.         term.setCursorPos(screenWidth - directory:len(), screenHeight)
  204.         term.write(directory)
  205.  
  206.         term.setTextColor(colors.white)
  207.         term.setBackgroundColor(colors.black)
  208. end
  209.  
  210. -- Gets a list of arguments from the user using read(), then returns those values
  211. -- in a table.
  212. function getArgumentsForProgram()
  213.         term.setCursorPos(7, screenHeight)
  214.         term.setTextColor(colors.yellow)
  215.         term.setBackgroundColor(colors.purple)
  216.        
  217.         term.write(string.rep(' ', screenWidth - 7))
  218.         term.setCursorPos(7, screenHeight)
  219.         term.write("Args: ")
  220.  
  221.         local stringArguments = read()
  222.         local tableArguments = splitArgs(stringArguments, ' ')
  223.  
  224.         term.setTextColor(colors.white)
  225.         term.setBackgroundColor(colors.black)
  226.         return tableArguments
  227. end
  228. --=END SCREEN UTILITY FUNCTIONS==========--
  229.  
  230.  
  231.  
  232. --=MOUSE HANDLING FUNCTIONS==============--
  233. -- Checks if a mouse click was on an item drawn to the screen.
  234. -- Returns the index of the item clicked, or nil if no item was clicked.
  235. function getItemClicked(xClickPos, yClickPos)
  236.         local itemIndex = nil -- The index of the item clicked if any.
  237.         for index, item in pairs(tScreenBuffer) do
  238.                 -- Check if the click was on this item's line.
  239.                 if yClickPos == item.yPos then
  240.                         -- Check if the click's xPosition was within the area of the item.
  241.                         if xClickPos <= item.xPos + item.text:len() - 1 and xClickPos >= item.xPos then
  242.                                 itemIndex = index
  243.                         end
  244.                 end
  245.         end
  246.         -- If any item was clicked, then the value returned will not be nil.
  247.         return itemIndex
  248. end
  249.  
  250. -- Performs the appropriate action in the case of a click.
  251. function handleClick(xClickPos, yClickPos, clickType)
  252.         -- If there was a right click and we are deeper than the root directory, then back up one directory.
  253.         if clickType == 2 then
  254.                 handleRightClick()
  255.                 scrollOffset = 1
  256.                 return
  257.         end
  258.  
  259.         local itemIndex = getItemClicked(xClickPos, yClickPos)
  260.         -- If an option was clicked then handle it.
  261.         if itemIndex then
  262.                 -- If the start option was clicked some directory, no matter what, is expanded, then
  263.                 --- dump and reinitalize the buffer.
  264.                 if itemIndex == 1 then
  265.                         if #tScreenBuffer > 1 then
  266.                                 initalizeBuffer()
  267.                                 currentDirectory = nil
  268.                                 scrollOffset = 1
  269.                         else
  270.                                 initalizeBuffer()
  271.                                 currentDirectory = ""
  272.                                 scrollOffset = 1
  273.                         end
  274.                 else
  275.                         -- Loop through the contents of the screen buffer and check for items clicked.
  276.                         for index, item in pairs(tScreenBuffer) do
  277.                                 if itemIndex == index then
  278.                                         -- If the item clicked was a file, then run the file.
  279.                                         if not fs.isDir(item.path) then
  280.                                                 -- Get arguments for the program to be called, then run the program.
  281.                                                 local tableArguments = getArgumentsForProgram()
  282.                                                 clearScreen()
  283.                                                 shell.run(item.path, unpack(tableArguments))
  284.                                         -- If the item clicked was a directory, then reinitialize the buffer and
  285.                                         -- expanded the directory.
  286.                                         else
  287.                                                 initalizeBuffer()
  288.                                                 currentDirectory = item.path
  289.                                         end
  290.                                 end
  291.                         end
  292.                 end
  293.         -- If no item was clicked, then dump and reinitialize the screen buffer.
  294.         else
  295.                 initalizeBuffer()
  296.                 currentDirectory = nil
  297.                 scrollOffset = 1
  298.                 return
  299.         end
  300.  
  301.         -- Draw the current directory.
  302.         -- If the current directory has value 'nil' then we can assume this is the start menu; don't draw any directory.
  303.         if currentDirectory then
  304.                 drawDirectoryContents(currentDirectory, 1, screenHeight - 1)
  305.         end
  306. end
  307.  
  308. -- Handles right clicks appropriately.
  309. function handleRightClick()
  310.         if #tScreenBuffer > 1 then
  311.                 if currentDirectory then
  312.                         -- If the directory is the root, then initialize the buffer so only the start screen is displayed.
  313.                         if currentDirectory == '/' or currentDirectory == "" then
  314.                                 currentDirectory = nil
  315.                                 initalizeBuffer()
  316.                         -- If the directory is anything else, then back up the directory once.
  317.                         else
  318.                                 local namePos = currentDirectory:find(fs.getName(currentDirectory))
  319.  
  320.                                 currentDirectory = currentDirectory:sub(1, namePos - 1)
  321.  
  322.                                 initalizeBuffer()
  323.                                 drawDirectoryContents(currentDirectory, 1, screenHeight - 1)
  324.                         end
  325.                 end
  326.         end
  327. end
  328.  
  329. -- Handles the scrolling of the screen via mouse clicks.
  330. -- NOTE: Uses a global variable to handle the scroll offsetting.
  331. function handleScrollingWithMouse(clickedUp, directoryPath)
  332.         local directoryContents = appendContentsLengths(fs.list(directoryPath), directoryPath)
  333.         -- ^^The contents of the directory that will be scrollable.
  334.         local scrolledDirectoryContents = {} -- The appended directory contents that incorporates the scroll offset.
  335.  
  336.         -- If the user clicked up, then scroll the list up one item.
  337.         if clickedUp then
  338.                 -- Make sure the screen can be scrolled up more.
  339.                 if #directoryContents - scrollOffset + 1 > screenHeight - 1 then
  340.                         -- Get a scrolled up version of the directory contents.
  341.                         scrollOffset = scrollOffset + 1
  342.                 end
  343.         -- If the user clicked down, then scroll the list down one item.
  344.         else
  345.                 -- Make sure the screen can be scrolled down more.
  346.                 if #directoryContents + scrollOffset - 1 > #directoryContents then
  347.                         -- Get a scrolled down version of the directory contents.
  348.                         scrollOffset = scrollOffset - 1
  349.                 end
  350.         end
  351.  
  352.         -- Append the directory contents with the scrolled directory contents table.
  353.         for index = scrollOffset, #directoryContents do
  354.                 table.insert(scrolledDirectoryContents, directoryContents[index])
  355.         end
  356.  
  357.         return scrolledDirectoryContents
  358. end
  359. --=END MOUSE HANDLING FUNCTIONS==========--
  360.  
  361. --=LOGOS=================================--
  362. -- Draws the cannonShell logo with the screen buffer.
  363. function drawLogo()
  364.         local tLogo = {
  365.                 [1] = "__   __                          ",
  366.                 [2] = "\\ \\ / /                          ",
  367.                 [3] = " \\ v / __  ___  ___  _____  _  __",
  368.                 [4] = "  > < /  \\/ / |/ / |/ / _ \\| |/ /",
  369.                 [5] = " / ^ ( ()  <| / /| / ( (_) ) / / ",
  370.                 [6] = "/_/ \\_\\__/\\_\\__/ |__/ \\___/|__/  "
  371.         }
  372.  
  373.         term.setTextColor(colors.cyan)
  374.         for line = 4, #tLogo + 4 do
  375.                 term.setCursorPos(screenWidth/2 - tLogo[1]:len()/2, line)
  376.                 term.write(tLogo[line - 3])
  377.         end
  378. end
  379. --=END LOGOS=============================--
  380.  
  381. initalizeBuffer() -- Add the start option to the buffer.
  382. while true do
  383.         clearScreen()
  384.         drawLogo()
  385.         redrawBuffer()
  386.  
  387.         -- if currentDirectory then displayPath() end -- Display the current directory that we are in.
  388.  
  389.         local event, clickType, xClickPos, yClickPos = os.pullEvent()
  390.  
  391.         -- If the mouse was clicked then handle aforementioned click appropriately.
  392.         if event == "mouse_click" then
  393.                 handleClick(xClickPos, yClickPos, clickType)
  394.         -- If the mouse wheel was scrolled then handle the scroll appropriately.
  395.         elseif event == "mouse_scroll" then
  396.                 -- If the directory is not the start menu, then check for scrolling.
  397.                 if currentDirectory then
  398.                         initalizeBuffer()
  399.                         local scrollType = clickType == -1 and true or false
  400.                         drawTable(handleScrollingWithMouse(scrollType, currentDirectory), 1, screenHeight - 1)
  401.                 end
  402.         -- If the exit key was pressed, then exit the program.
  403.         elseif event == "key" then
  404.                 -- If escape was pressed then exit the program.
  405.                 if clickType == EXIT_KEY then
  406.                         clearScreen()
  407.                         term.setTextColor(colors.yellow)
  408.  
  409.                         print(os.version())
  410.                         term.setTextColor(colors.white)
  411.  
  412.                         break
  413.                 end
  414.         end
  415. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement