Last active
August 29, 2015 14:18
-
-
Save LeoVerto/ec73db6cf75e18992be1 to your computer and use it in GitHub Desktop.
Custom ComputerCraft shell based on a file browser by some other guy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Initialize some variables for later | |
local xMax, yMax = term.getSize() | |
local row = 4, 3, 1 | |
-- Returns a table of possible icon origin positions | |
function originTable(maxCol, maxRow) | |
local tOrigins = {} | |
for i=1, maxRow do | |
for j=1, maxCol do | |
table.insert(tOrigins, {3+9*(j-1), 2+6*(i-1)}) | |
end | |
end | |
return tOrigins | |
end | |
-- Array search function | |
function contains(t, key) | |
for k,v in pairs(t) do | |
if v == key then | |
return true | |
end | |
end | |
return false | |
end | |
-- Writes text to the specified position | |
function output(x, y, sText, color) | |
if color ~= nil then | |
term.setTextColor(color) | |
else | |
term.setTextColor(colors.white) | |
end | |
term.setCursorPos(x, y) | |
term.write(sText) | |
end | |
-- Draws a rectangular border | |
function drawBorder(x, y, xLen, yLen, color) | |
output(x, y, "/", color) | |
output(x+xLen-1, y, "\\", color) | |
output(x, y+yLen-1, "\\", color) | |
output(x+xLen-1, y+yLen-1, "/", color) | |
for i=x+1, x+xLen-2 do | |
output(i, y, "-", color) | |
output(i, y+yLen-1, "-", color) | |
end | |
for i=y+1, y+yLen-2 do | |
output(x, i, "|", color) | |
output(x+xLen-1, i, "|", color) | |
end | |
end | |
-- Draws a centered menu and returns origin, length, and height | |
function drawMenu(tMenu, closeButton) | |
local length, height = 0, #tMenu+2 | |
for _, sLine in ipairs(tMenu) do | |
if string.len(sLine) >= length then | |
length = string.len(sLine)+2 | |
end | |
end | |
local sBlank = "" | |
for i=1, length-2 do sBlank = sBlank.." " end | |
local x, y = math.floor(xMax/2) - math.ceil(length/2), math.floor(yMax/2) - math.ceil(#tMenu/2) | |
drawBorder(x, y, length, height, colors.lime) | |
if closeButton then | |
output(x+length-3, y, "X", colors.red) | |
end | |
for i, sLine in ipairs(tMenu) do | |
output(x+1, y+i, sBlank) | |
output(x+1, y+i, sLine) | |
end | |
return {x, y}, length, height | |
end | |
-- Runs a selection menu and returns a number or nil | |
function runSelectionMenu(...) | |
local tArgs = {...} | |
local tMenu = {} | |
for _, sOption in ipairs(tArgs) do | |
table.insert(tMenu, " ( ) "..sOption.." ") | |
end | |
table.insert(tMenu, " [Okay] [Cancel] ") | |
local origin, length, height = drawMenu(tMenu, true) | |
local selection = 1 | |
local x, y = origin[1]+3, origin[2]+1 | |
local bMenu = true | |
while bMenu do | |
output(origin[1]+3, origin[2]+selection, "#") | |
local sEvent, ret1, ret2, ret3 = os.pullEventRaw() | |
if sEvent == "mouse_click" and ret1 == 1 then | |
local x, y = ret2, ret3 | |
if y == origin[2] and x == origin[1]+length-3 then | |
bMenu = false | |
selection = nil | |
elseif x > origin[1] and x < origin[1]+length-1 and y > origin[2] and y < origin[2]+height-1 then | |
if y < origin[2]+height-2 then | |
selection = y - origin[2] | |
elseif x > origin[1]+1 and x < origin[1]+8 then | |
bMenu = false | |
elseif x > origin[1]+8 and x < origin[1]+17 then | |
bMenu = false | |
selection = nil | |
end | |
drawMenu(tMenu, true) | |
end | |
end | |
end | |
return selection | |
end | |
-- Runs a text menu and returns a string | |
function runTextMenu(sPrompt, sStartText) | |
local tMenu = {" "..sPrompt..":", " { } ", " [Done] "} | |
local origin, length, height = drawMenu(tMenu) | |
local sText = "" | |
if sStartText then sText = sStartText end | |
local bMenu = true | |
while bMenu do | |
output(origin[1]+3, origin[2]+2, string.sub(sText, 1, 14)) | |
output(origin[1]+3, origin[2]+2, " ") | |
output(origin[1]+3, origin[2]+2, string.sub(sText, -14)) | |
local sEvent, ret1, ret2, ret3 = os.pullEventRaw() | |
if sEvent == "mouse_click" and ret1 == 1 then | |
local x, y = ret2, ret3 | |
if y == origin[2]+height-2 and x > origin[1]+1 and x < origin[1]+8 then | |
bMenu = false | |
end | |
elseif sEvent == "char" then | |
sText = sText .. ret1 | |
elseif sEvent == "key" then | |
if ret1 == 14 then | |
sText = string.sub(sText, 1, string.len(sText)-1) | |
end | |
end | |
end | |
return sText | |
end | |
-- Runs a confirmation menu and returns a boolean | |
function runConfirmMenu(...) | |
local tArgs = {...} | |
local tMenu = {} | |
for _, sLine in ipairs(tArgs) do | |
tMenu[#tMenu+1] = " "..sLine.." " | |
end | |
table.insert(tMenu, " [Yes] [No] ") | |
local origin, length, height = drawMenu(tMenu) | |
local bMenu, bConfirm = true | |
while bMenu do | |
local sEvent, ret1, ret2, ret3 = os.pullEventRaw() | |
if sEvent == "mouse_click" and ret1 == 1 then | |
local x, y = ret2, ret3 | |
if y == origin[2]+height-2 and x > origin[1]+1 and x < origin[1]+7 then | |
bMenu, bConfirm = false, true | |
elseif y == origin[2]+height-2 and x > origin[1]+7 and x < origin[1]+12 then | |
bMenu, bConfirm = false, false | |
end | |
end | |
end | |
return bConfirm | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
A graphical user interface file browser (guifb) | |
Version 1.4.0 | |
by inventor2514 | |
]]-- | |
if not term.isColor then | |
print("An advanced computer is required") | |
return | |
else | |
if not term.isColor() then | |
print("An advanced computer is required") | |
return | |
end | |
end | |
-- Configuration variables | |
local sStartDir = "" | |
local sEditPath = "/.programs/cedit" | |
local sProgramDir = "Programs" | |
local sApiDir = ".apis" | |
-- Initialize some variables for later | |
os.loadAPI("nukeAPI") | |
local tDirIcon = {"", " /\\____", "| |", "| |", "|______|"} | |
local tFileIcon = {" ______", "| |", "| ~~~~ |", "| ~~~~ |", "|______|"} | |
local tImageIcon = {" ______", "|()__ |", "| /__\\ |", "| |__| |", "|______|"} | |
local tAnimationIcon = {" ______", "| |", "| |", "| |", "|______|"} | |
local tFileExtentions = {["image"] = {".img", ".nfp"}, ["animation"] = {".gif", ".nfa"}} | |
local tFileBlacklist = {"nukeShell", "rom"} | |
local xMax, yMax = term.getSize() | |
local row = 4, 3, 1 | |
local sDir = sStartDir | |
local maxCol, maxRow = 1, 1 | |
local tOrigins = {} | |
local tData = {} | |
local tDebug ={"Initialized"} | |
sClipboard = "" | |
local sVersion = "1.0" | |
-- Returns the maximum columns and rows of icons | |
local function iconLimits() | |
local i, maxCol = 3, 0 | |
while i <= xMax-9 do | |
i = i + 9 | |
maxCol = maxCol + 1 | |
end | |
local i, maxRow = 2, 0 | |
while i <= yMax-6 do | |
i = i + 6 | |
maxRow = maxRow + 1 | |
end | |
return maxCol, maxRow | |
end | |
-- Returns a table containing file data in {sName, bDir} format | |
local function fileData(sDir) | |
local tList = fs.list(sDir) | |
local tData = {} | |
for i,sName in ipairs(tList) do | |
if string.sub(sName, 1, 1) ~= "." and not nukeAPI.contains(tFileBlacklist, sName) then | |
local sPath = fs.combine(sDir, sName) | |
local sFiletype = nil | |
local sNewName = sName | |
local sExtension = sName:sub(-4) | |
if fs.isDir(sPath) then | |
sFiletype = "dir" | |
elseif nukeAPI.contains(tFileExtentions["image"], sExtension) then | |
sFiletype = "image" | |
sNewName = sName:sub(1, sName:len()-4) | |
elseif nukeAPI.contains(tFileExtentions["animation"], sExtension) then | |
sFiletype = "animation" | |
sNewName = sName:sub(1, sName:len()-4) | |
elseif sDir == sProgramDir then | |
sFiletype = "link" | |
end | |
table.insert(tData, {sNewName, sFiletype}) | |
end | |
end | |
return tData | |
end | |
-- Draws a window-like border and buttons | |
local function drawMainBorder() | |
nukeAPI.drawBorder(1, 1, xMax, yMax, colors.lime) | |
nukeAPI.output(xMax-4, 1, "X", colors.red) | |
nukeAPI.output(xMax-6, 1, "<", colors.blue) | |
nukeAPI.output(xMax, 3, "*", colors.lightBlue) | |
nukeAPI.output(xMax, yMax-2, "*", colors.lightBlue) | |
nukeAPI.output(3, 1, "[NukeShell "..sVersion.."]", colors.lime) | |
end | |
-- Draws an icon at the specified position | |
local function drawIcon(x, y, tIcon, sName) | |
term.setTextColor(colors.white) | |
local tIcon = tIcon | |
for i, s in ipairs(tIcon) do | |
nukeAPI.output(x, y+i-1, s) | |
end | |
if sName then nukeAPI.output(x, y+#tIcon, sName) end | |
end | |
-- Draws icons to represent file data | |
local function drawIcons() | |
local pos = 1 + maxCol*(row-1) | |
local limit = pos-1+maxCol*maxRow | |
if #tData < limit then limit = #tData end | |
local icon = 1 | |
for i = pos, limit do | |
local x, y, sName = tOrigins[icon][1], tOrigins[icon][2], string.sub(tData[i][1], 1, 8) | |
if tData[i][2] == "dir" then | |
drawIcon(x, y, tDirIcon, sName) | |
elseif tData[i][2] == "image" then | |
drawIcon(x, y, tImageIcon, sName) | |
elseif tData[i][2] == "animation" then | |
drawIcon(x, y, tAnimationIcon, sName) | |
else | |
drawIcon(x, y, tFileIcon, sName) | |
end | |
icon = icon + 1 | |
end | |
end | |
-- Opens a commandline | |
local function runCmd() | |
nukeAPI.drawBorder(xMax/4, yMax/4, 10, 10, colors.lime) | |
-- nukeAPI.output(xMax/2-2, 1, "X", colors.red) | |
print("CMD") | |
end | |
-- Redraws the border and icons | |
local function redraw() | |
term.clear() | |
term.setCursorBlink(false) | |
drawIcons() | |
drawMainBorder() | |
end | |
-- Recalculate the file data and icon variables | |
local function recalculate() | |
row = 1 | |
tData = fileData(sDir) | |
maxCol, maxRow = iconLimits() | |
tOrigins = nukeAPI.originTable(maxCol, maxRow) | |
end | |
-- Returns the data associated with an icon or nil | |
local function iconData(x, y) | |
local pos = maxCol*(row-1) | |
for i=1, maxCol*maxRow do | |
local xMin, yMin = tOrigins[i][1], tOrigins[i][2] | |
if x >= xMin and y >= yMin and x <= xMin+8 and y <= yMin+5 then | |
tIconData = tData[pos+i] | |
if tIconData then | |
if fs.exists(fs.combine(sDir, tIconData[1])) then | |
return tIconData | |
else | |
recalculate() | |
redraw() | |
end | |
end | |
end | |
end | |
return nil | |
end | |
-- Load all files from api dir as APIs | |
local function loadApis() | |
local tApis = fs.list(sApiDir) | |
for i=1, #tApis do | |
os.loadAPI(sApiDir.."/"..tApis[i]) | |
end | |
end | |
-- Main program loop | |
local bRun = true | |
recalculate() | |
redraw() | |
local bCursorToggle = false | |
loadApis() | |
while bRun do | |
local sEvent, ret1, ret2, ret3 = os.pullEventRaw() | |
if sEvent == "mouse_click" then | |
local x, y = ret2, ret3 | |
-- left-click | |
if ret1 == 1 then | |
-- close button | |
if x == xMax-4 and y == 1 then | |
bRun = false | |
-- back button | |
elseif x == xMax-6 and y == 1 then | |
if sDir ~= "" then | |
sDir = fs.combine(sDir, "..") | |
recalculate() | |
redraw() | |
end | |
-- scroll up button | |
elseif x == xMax and y == 3 then | |
if row > 1 then | |
row = row - 1 | |
redraw() | |
end | |
-- scroll down button | |
elseif x == xMax and y == yMax-2 then | |
if row < (math.ceil(#tData/maxCol)-(maxRow-1)) then | |
row = row + 1 | |
redraw() | |
end | |
-- file/dir icon | |
elseif iconData(x, y) then | |
local tIconData = iconData(x, y) | |
if tIconData[2] == "dir" then | |
sDir = fs.combine(sDir, tIconData[1]) | |
recalculate() | |
redraw() | |
elseif tIconData[2] ~= nil then | |
local sPath = "/" .. fs.combine(sDir, tIconData[1]) | |
term.clear() | |
term.setCursorPos(1,1) | |
shell.run(sPath) | |
redraw() | |
sleep(2) | |
else | |
local sPath = "/" .. fs.combine(sDir, tIconData[1]) | |
shell.run(sEditPath, sPath) | |
redraw() | |
end | |
end | |
-- right-click | |
elseif ret1 == 2 then | |
if x == 1 or y == 1 or x == xMax or y == yMax then | |
-- do nothing | |
elseif iconData(x, y) then | |
local tIconData = iconData(x, y) | |
if tIconData[2] == "dir" then | |
local sDiskSide = nil | |
for i,sSide in ipairs( rs.getSides() ) do | |
if disk.isPresent(sSide) then | |
if tIconData[1] == disk.getMountPath(sSide) then | |
sDiskSide = sSide | |
end | |
end | |
end | |
if sDiskSide then | |
local selection = nukeAPI.runSelectionMenu("Enter Disk", "Label Disk", "Copy Disk", "Eject Disk") | |
if selection and fs.exists(fs.combine(sDir, tIconData[1])) then | |
-- enter disk | |
if selection == 1 then | |
sDir = fs.combine(sDir, tIconData[1]) | |
recalculate() | |
redraw() | |
-- label disk | |
elseif selection == 2 then | |
local sLabel = nukeAPI.runTextMenu("Disk Label", disk.getLabel(sDiskSide)) | |
if sLabel ~= "" and disk.isPresent(sDiskSide) then | |
disk.setLabel(sDiskSide, sLabel) | |
end | |
-- copy disk | |
elseif selection == 3 then | |
if fs.exists(".copy") then fs.delete(".copy") end | |
fs.copy(fs.combine(sDir, tIconData[1]), ".copy") | |
sClipboard = tIconData[1] | |
-- eject disk | |
elseif selection == 4 then | |
disk.eject(sDiskSide) | |
sleep(.3) | |
fs.delete(sDiskSide) | |
recalculate() | |
redraw() | |
end | |
end | |
else | |
local selection = nukeAPI.runSelectionMenu("Open Dir", "Rename Dir", "Copy Dir", "Delete Dir") | |
if selection then | |
-- enter dir | |
if selection == 1 then | |
sDir = fs.combine(sDir, tIconData[1]) | |
recalculate() | |
redraw() | |
-- rename dir | |
elseif selection == 2 then | |
if not fs.isReadOnly(fs.combine(sDir, tIconData[1])) and fs.combine(sDir, tIconData[1]) ~= sProgramDir then | |
local sName = nukeAPI.runTextMenu("Dir Name", tIconData[1]) | |
if sName ~= "" and sName ~= tIconData[1] and not fs.isReadOnly(fs.combine(sDir, sName)) then | |
local bConfirm = true | |
if fs.exists(fs.combine(sDir,sName)) then | |
bConfirm = nukeAPI.runConfirmMenu("Overwrite existing", "file with that name?") | |
end | |
if bConfirm then | |
fs.move(fs.combine(sDir, tIconData[1]), fs.combine(sDir, sName)) | |
recalculate() | |
end | |
end | |
end | |
-- copy dir | |
elseif selection == 3 then | |
if fs.exists(".copy") then fs.delete(".copy") end | |
fs.copy(fs.combine(sDir, tIconData[1]), ".copy") | |
sClipboard = tIconData[1] | |
-- delete dir | |
elseif selection == 4 then | |
if not fs.isReadOnly(fs.combine(sDir, tIconData[1])) then | |
fs.delete(fs.combine(sDir, tIconData[1])) | |
recalculate() | |
end | |
end | |
end | |
end | |
else | |
local selection = nukeAPI.runSelectionMenu("Edit File", "Run File", "Rename File", "Copy File", "Delete File") | |
if selection then | |
-- edit file | |
if selection == 1 then | |
local sPath = "/" .. fs.combine(sDir, tIconData[1]) | |
shell.run(sEditPath, sPath) | |
-- run file | |
elseif selection == 2 then | |
local sPath = "/" .. fs.combine(sDir, tIconData[1]) | |
local sArgs = nukeAPI.runTextMenu("Arguments") | |
term.clear() | |
term.setCursorPos(1, 1) | |
if sArgs ~= "" then | |
local tArgs = {} | |
for sMatch in string.gmatch(sArgs, "[^ ]+") do | |
table.insert(tArgs, sMatch) | |
end | |
shell.run(sPath, unpack(tArgs)) | |
else | |
shell.run(sPath) | |
end | |
sleep(2) | |
print("= Press any key to return =") | |
os.pullEventRaw("key") | |
-- rename file | |
elseif selection == 3 then | |
if not fs.isReadOnly(fs.combine(sDir, tIconData[1])) then | |
local sName = nukeAPI.runTextMenu("File Name", tIconData[1]) | |
if sName ~= "" and sName ~= tIconData[1] and not fs.isReadOnly(fs.combine(sDir, sName)) then | |
local bConfirm = true | |
if fs.exists(fs.combine(sDir,sName)) then | |
bConfirm = nukeAPI.runConfirmMenu("Overwrite existing", "file with that name?") | |
end | |
if bConfirm then | |
fs.move(fs.combine(sDir, tIconData[1]), fs.combine(sDir, sName)) | |
recalculate() | |
end | |
end | |
end | |
-- copy file | |
elseif selection == 4 then | |
if fs.exists(".copy") then fs.delete(".copy") end | |
fs.copy(fs.combine(sDir, tIconData[1]), ".copy") | |
sClipboard = tIconData[1] | |
-- delete file | |
elseif selection == 5 then | |
if not fs.isReadOnly(fs.combine(sDir, tIconData[1])) then | |
fs.delete(fs.combine(sDir, tIconData[1])) | |
recalculate() | |
end | |
end | |
end | |
end | |
else | |
local selection = nukeAPI.runSelectionMenu("New Dir", "New File", "Open Commandline", "Paste") | |
if selection then | |
-- new dir | |
if selection == 1 then | |
local sName = nukeAPI.runTextMenu("Dir Name") | |
if sName ~= "" and not fs.isReadOnly(fs.combine(sDir, sName)) then | |
local bConfirm = true | |
if fs.exists(fs.combine(sDir,sName)) then | |
bConfirm = nukeAPI.runConfirmMenu("Overwrite existing", "file with that name?") | |
end | |
if bConfirm then | |
fs.makeDir(fs.combine(sDir,sName)) | |
recalculate() | |
end | |
end | |
-- new file | |
elseif selection == 2 then | |
local sName = nukeAPI.runTextMenu("File Name") | |
if sName ~= "" and not fs.isReadOnly(fs.combine(sDir, sName)) then | |
local bConfirm = true | |
if fs.exists(fs.combine(sDir,sName)) then | |
bConfirm = nukeAPI.runConfirmMenu("Overwrite existing", "file with that name?") | |
end | |
if bConfirm then | |
local file = io.open(fs.combine(sDir, sName), "w") | |
file:write("\n") | |
file:close() | |
recalculate() | |
end | |
end | |
-- open commandline | |
elseif selection == 3 then | |
runCmd() | |
-- paste | |
elseif selection == 4 then | |
local sName = nukeAPI.runTextMenu("Paste Name", sClipboard) | |
if sName ~= "" and not fs.isReadOnly(fs.combine(sDir, sName)) then | |
local bConfirm = true | |
if fs.exists(fs.combine(sDir,sName)) then | |
bConfirm = nukeAPI.runConfirmMenu("Overwrite existing", "file with that name?") | |
end | |
if bConfirm then | |
fs.delete(fs.combine(sDir, sName)) | |
fs.copy(".copy", fs.combine(sDir, sName)) | |
recalculate() | |
end | |
end | |
end | |
end | |
end | |
redraw() | |
end | |
elseif sEvent == "mouse_scroll" then | |
-- scroll down | |
if ret1 == 1 then | |
if row < (math.ceil(#tData/maxCol)-(maxRow-1)) then | |
row = row + 1 | |
redraw() | |
end | |
-- scroll up | |
else | |
if row > 1 then | |
row = row - 1 | |
redraw() | |
end | |
end | |
elseif sEvent == "key" then | |
local key = ret1 | |
-- page up "scroll up" | |
if key == 201 then | |
if row > 1 then | |
row = row - 1 | |
redraw() | |
end | |
-- page down "scroll down" | |
elseif key == 209 then | |
if row < (math.ceil(#tData/maxCol)-(maxRow-1)) then | |
row = row + 1 | |
redraw() | |
end | |
-- backspace "back" | |
elseif key == 14 then | |
if sDir ~= "" then | |
sDir = fs.combine(sDir, "..") | |
recalculate() | |
redraw() | |
end | |
-- end "close" | |
elseif key == 207 then | |
bRun = false | |
end | |
end | |
end | |
-- Cleanup | |
if fs.exists(".copy") then fs.delete(".copy") end | |
term.clear() | |
term.setCursorPos(1, 1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment