Advertisement
asders

mini_pc_os

Aug 26th, 2019
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. local tArgs = { ... }
  2. local version = "a0.2"
  3.  
  4. --CONFIG
  5. --colors of lattix
  6. local lat_theme = {
  7.     menu_back = colors.green;
  8.     menu_text = colors.black;
  9.     main_back = colors.black;
  10.     main_text = colors.green;
  11.     selected_text = colors.cyan;
  12.     dir_text = colors.blue;
  13.     multiselect_back = colors.lightGray;
  14.     blank_back = colors.black;
  15.     blank_text = colors.green;
  16. }
  17. --colors of lattix on basic comps
  18. local lat_basic_theme = {
  19.     menu_back = colors.gray;
  20.     menu_text = colors.black;
  21.     main_back = colors.black;
  22.     main_text = colors.green;
  23.     selected_text = colors.lightGray;
  24.     dir_text = colors.black;
  25.     multiselect_back = colors.white;
  26.     blank_back = colors.white;
  27.     blank_text = colors.black;
  28. }
  29. local dirprefix = "" --this text gets added before dirs
  30. local basic_dirprefix = "[] " --this text gets added before dirs on non-advanced computers
  31. local selectprefix = "" --this text gets added before selected things (even in multiselect)
  32. local basic_selectprefix = "> " --this text gets added before selected things (even in multiselect) on non-advanced computers
  33. --which programs to use to open .<xtension> files. Special codes: "run" and "api" and special extension "def" - def will be used for every extension not listed (def must be something), run runs it without args, api loads it with os.loadAPI (yes ikr very useful rn since os.loadAPI doesn't like weird extensions). The empty string indicates no extension
  34. --the file is opened by running the program chosen below with arg #1 being the path to the file
  35. local defaultPrograms = {
  36.     [""] = "/rom/programs/edit.lua",
  37.     ["txt"] = "/rom/programs/edit.lua",
  38.     ["cfg"] = "/rom/programs/edit.lua",
  39.     ["config"] = "/rom/programs/edit.lua",
  40.     ["nfp"] = "/rom/programs/fun/advanced/paint.lua",
  41.     ["lua"] = "run",
  42. --  ["api"] = "api", --if a custom OS sets up os.loadAPI to work with .api and stores its apis in .api then this could work
  43.     ["def"] = "/rom/programs/edit.lua"
  44. }
  45. --which programs can be used to create files (think clicking the New... button) Special code: choose means the program is chosen manually through a dialog
  46. --these programs are run with the path to the file that is to be created as first argument
  47. local editors = {
  48.     ["Other"] = "choose",
  49.     ["NFP painting"] = "/rom/programs/fun/advanced/paint.lua",
  50.     ["Lua script"] = "/rom/programs/edit.lua",
  51.     ["Text file"] = "/rom/programs/edit.lua"
  52.    
  53.    
  54.    
  55. }
  56. --Extensions for the above editors
  57. local editors_extensions = {
  58.     ["NFP painting"] = "nfp",
  59.     ["Lua script"] = "lua",
  60.     ["Text file"] = "txt"
  61.    
  62.    
  63. }
  64.  
  65. local surfacepath = "surface"
  66.  
  67. --code adopted and modified from CC's source code
  68. local function pastebinGet(paste, path)
  69.    
  70.     if not http then
  71.         error("HTTP disabled", 2)
  72.     end
  73.     --- Attempts to guess the pastebin ID from the given code or URL
  74.     local extractId = function (paste)
  75.         local patterns = {
  76.             "^([%a%d]+)$",
  77.             "^https?://pastebin.com/([%a%d]+)$",
  78.             "^pastebin.com/([%a%d]+)$",
  79.             "^https?://pastebin.com/raw/([%a%d]+)$",
  80.             "^pastebin.com/raw/([%a%d]+)$",
  81.         }
  82.  
  83.         for i = 1, #patterns do
  84.             local code = paste:match( patterns[i] )
  85.             if code then return code end
  86.         end
  87.  
  88.         return nil
  89.     end
  90.  
  91.     local get = function (url)
  92.         local paste = extractId( url )
  93.         if not paste then
  94.             error( "Invalid pastebin code.", 0 )
  95.             return
  96.         end
  97.  
  98.         -- Add a cache buster so that spam protection is re-checked
  99.         local cacheBuster = ("%x"):format(math.random(0, 2^30))
  100.         local response, err = http.get(
  101.             "https://pastebin.com/raw/"..textutils.urlEncode( paste ).."?cb="..cacheBuster
  102.         )
  103.  
  104.         if response then
  105.             -- If spam protection is activated, we get redirected to /paste with Content-Type: text/html
  106.             local headers = response.getResponseHeaders()
  107.             if not headers["Content-Type"] or not headers["Content-Type"]:find( "^text/plain" ) then
  108.                 error( "Pastebin blocked the download due to spam protection. Please complete the captcha in a web browser: https://pastebin.com/" .. textutils.urlEncode( paste ) , 0)
  109.                 return
  110.             end
  111.  
  112.             local sResponse = response.readAll()
  113.             response.close()
  114.             return sResponse
  115.         else
  116.             error (err, 0)
  117.         end
  118.     end
  119.  
  120.     -- Determine file to download
  121.     local sCode = paste
  122.     local sPath = path
  123.     if fs.exists( sPath ) then
  124.         error( "File already exists", 0 )
  125.         return
  126.     end
  127.  
  128.     -- GET the contents from pastebin
  129.     local res = get(sCode)
  130.     if res then
  131.         local file = fs.open( sPath, "w" )
  132.         file.write( res )
  133.         file.close()
  134.     end
  135. end
  136. local function wGet(url, sPath)
  137.  
  138.     if not http then
  139.         error( "HTTP disabled", 2 )
  140.     end
  141.  
  142.     local function getFilename( sUrl )
  143.         sUrl = sUrl:gsub( "[#?].*" , "" ):gsub( "/+$" , "" )
  144.         return sUrl:match( "/([^/]+)$" )
  145.     end
  146.  
  147.     local function get( sUrl )
  148.         -- Check if the URL is valid
  149.         local ok, err = http.checkURL( url )
  150.         if not ok then
  151.             error( err or "Invalid URL.", 0 )
  152.             return
  153.         end
  154.  
  155.         local response = http.get( sUrl , nil , true )
  156.         if not response then
  157.             error( "Failed." )
  158.         end
  159.  
  160.         local sResponse = response.readAll()
  161.         response.close()
  162.         return sResponse
  163.     end
  164.  
  165.     if fs.exists( sPath ) then
  166.         error( "File already exists", 0 )
  167.         return
  168.     end
  169.  
  170.     local res = get(url)
  171.     if not res then return end
  172.  
  173.     local file = fs.open( sPath, "wb" )
  174.     file.write( res )
  175.     file.close()
  176. end
  177.  
  178.  
  179. local function program()
  180.  
  181. --api loading
  182. if not surface then
  183.     if fs.exists(surfacepath) then
  184.         os.loadAPI(surfacepath)
  185.     else
  186.         print("installing missing component: " .. surfacepath)
  187.         pastebinGet("J2Y288mW", surfacepath)
  188.         os.loadAPI(surfacepath)
  189.     end
  190. end
  191. --term args
  192. local home --the directory Lattix is opened in
  193. local root --the root directory that you're not allowed to go above
  194.  
  195. if #tArgs > 0 then
  196.     home = tArgs[1]
  197. else
  198.     home = ""
  199. end
  200. if #tArgs > 1 then
  201.     root = tArgs[2]
  202. else
  203.     root = ""
  204. end
  205.  
  206. root = fs.combine("", root)
  207. home = fs.combine("", home)
  208.  
  209. --variables
  210. local clipboard --a file path/table of files
  211. local clip_cut = false -- original file(s) will be deleted if this is true
  212. local history = {}
  213. local w,h = term.getSize()
  214.  
  215.  
  216.  
  217. --setting up path
  218. if not term.isColor() then
  219.     lat_theme = lat_basic_theme
  220.     dirprefix = basic_dirprefix
  221.     selectprefix = basic_selectprefix
  222. end
  223. local path = root
  224.  
  225. if fs.isDir(fs.combine(root,home)) then
  226.     path = fs.combine(root,home)
  227. elseif fs.isDir(root) then
  228.     path = root
  229. else
  230.     error("Not valid root folder",0)
  231. end
  232. local scroll = 0
  233. local selected = 0
  234. local endprogram = false
  235. local isCtrlDown = false
  236. local isShiftDown = false
  237. local selection = {}
  238. local isMultiSelect = false
  239.  
  240. local index = 0
  241. local empty = function() end
  242. local shell_input = ""
  243.  
  244. local itemNames = {} --displayed texts
  245. local itemPaths = {} --paths to items
  246. local customTitle --if not nil then the title of the program is replaced with this
  247.  
  248.  
  249. --setting up the second session
  250. local alt_histories = {
  251. {path}, {path}, {path}, {path}
  252. }
  253. local alt_paths = {
  254.     [1] = path,
  255.     [2] = path,
  256.     [3] = path,
  257.     [4] = path
  258. }
  259. local alt_scrolls = {
  260.     0,0,0,0
  261. }
  262. local alt_selected = {
  263.     0,0,0,0
  264. }
  265. local cSession = 1
  266.  
  267. local surf = surface.create(w,h," ",lat_theme.blank_back, lat_theme.blank_text)
  268.  
  269. local pathValid = true
  270.  
  271. --functions yet undefined:
  272. local redraw --so that any gui drawing can call a redraw
  273. local remap --so that redraw and remap can be overwritten if neccessary
  274. local restore --so that special popups that use the main loop can restore the file browser functionality
  275. --helpers
  276. local function mathdown(num)
  277.     if num % 1 ~= 0 then
  278.         return math.floor(num)
  279.     end
  280.     return num - 1
  281. end
  282. local function sort(path) --returns a sorted table of folder names and file names at path with folders being first
  283.     local tbl = {}
  284.     items = fs.list(path)
  285.     table.sort(items)
  286.     --insert dirs
  287.     for i=1,#items do
  288.         if fs.isDir(fs.combine(path,items[i])) then
  289.             tbl[#tbl+1] = fs.combine(path, items[i])
  290.         end
  291.     end
  292.     for i=1,#items do
  293.         if not fs.isDir(fs.combine(path,items[i])) then
  294.             tbl[#tbl+1] = fs.combine(path, items[i])
  295.         end
  296.     end
  297.     return tbl
  298. end
  299. local function formatNumber(num)
  300.     local extra = {"B","KB","MB"}
  301.     local eindex = 1
  302.     while num > 1024 do
  303.         num = num / 1024
  304.         num = math.floor(num)
  305.         eindex = eindex + 1
  306.     end
  307.     return tostring(num..extra[eindex])
  308. end
  309. local function posToIndex(maxn,x,y,scroll)
  310.     return y+scroll-2 < maxn and y+scroll-1 or 0
  311. end
  312. local function IndexToPos(index,scroll)
  313.     return 2,index-scroll
  314. end
  315. local function clear()
  316.     surf:clear()
  317.     surf:render()
  318.     term.setCursorPos(1,1)
  319. end
  320.  
  321. local function readFolderRaw(folder)
  322.     customTitle = nil --to quit all special view modes
  323.     pathValid = true
  324.     path = folder
  325.     itemNames = {}
  326.     itemPaths = {}
  327.    
  328.     local t = sort(folder)
  329.     for i=1,#t do
  330.         itemNames[i] = fs.getName(t[i])
  331.         itemPaths[i] = t[i]
  332.     end
  333.    
  334. end
  335.  
  336. local function switchFolderRaw(folder) --folder is in absolute path, does not add to history
  337.     readFolderRaw(folder)
  338.     selected = 0
  339.     scroll = 0
  340.     shell_input = ""
  341. end
  342.  
  343. local function switchFolder(folder) --folder is in absolute path
  344.     history[#history + 1] = path
  345.     switchFolderRaw(folder)
  346. end
  347.  
  348. local function waitForKeyOrClick()
  349.     while true do
  350.         local e = os.pullEvent()
  351.         if e == "key" or e == "mouse_click" then
  352.             break
  353.         end
  354.     end
  355. end
  356. local function split(inputstr, sep)
  357.         if sep == nil then
  358.                 sep = "%s"
  359.         end
  360.         local t={} ; i=1
  361.         for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
  362.                 t[i] = str
  363.                 i = i + 1
  364.         end
  365.         return t
  366. end
  367. local function switchSessions(newI)
  368.     --if newI == cSession then return end
  369.     alt_histories[cSession] = history
  370.     alt_paths[cSession] = path
  371.     alt_scrolls[cSession] = scroll
  372.     alt_selected[cSession] = selected
  373.     history = alt_histories[newI]
  374.     --after all we need to update the item list
  375.     switchFolderRaw(alt_paths[newI])
  376.     scroll = alt_scrolls[newI]
  377.     selected = alt_selected[newI]
  378.     cSession = newI
  379. end
  380. local function extensionRead(extension, width, bcolor, tcolor, dtext)
  381.     local x,y = term.getCursorPos()
  382.     term.setCursorBlink(true)
  383.     local t = dtext or ""
  384.     local i = #t
  385.     local scroll = 0
  386.     local tbasis
  387.    
  388.     while true do
  389.         surf:drawLine(x,y,x+width,y," ", bcolor,tcolor)
  390.         tbasis = t .. extension
  391.         if i < scroll then
  392.             scroll = i
  393.         end
  394.         if i > scroll + width - #extension then
  395.             scroll = i - width + #extension
  396.         end
  397.         if #tbasis > width then
  398.             if scroll + width > #tbasis then
  399.                 scroll = #tbasis - width
  400.             end
  401.             tbasis = tbasis:sub(1+scroll, width+scroll)
  402.         else
  403.             scroll = 0
  404.         end
  405.         surf:drawText(x,y,tbasis,bcolor,tcolor)
  406.         surf:render()
  407.         term.setCursorPos(x+i-scroll, y)
  408.         local ev = {os.pullEvent()}
  409.         local e,k = ev[1], ev[2]
  410.         repeat
  411.             if e == "paste" then
  412.                 e = "char"
  413.                 table.remove(ev, 1)
  414.                 k = table.concat(ev, " ")
  415.             end
  416.             if e == "char" then
  417.                 if i == #t then
  418.                     t = t .. k
  419.                     i = i + #k
  420.                 elseif i == 0 then
  421.                     t = k .. t
  422.                     i = i + #k
  423.                 else
  424.                     t = t:sub(1,i) .. k .. t:sub(i+1, #t)
  425.                     i = i + #k
  426.                 end
  427.             end
  428.            
  429.             if e == "key" then
  430.                 local mini = 0
  431.                 local maxi = #t
  432.                 if k == keys.left and i > mini then
  433.                     i = i - 1
  434.                     break
  435.                 end
  436.                 if k == keys.right and i < maxi then
  437.                     i = i + 1
  438.                     break
  439.                 end
  440.                
  441.                 if k == keys.up then
  442.                     i = mini
  443.                     break
  444.                 end
  445.                 if k == keys.down then
  446.                     i = maxi
  447.                     break
  448.                 end
  449.                 if k == keys.enter then
  450.                     term.setCursorBlink(false)
  451.                     return t .. extension
  452.                 end
  453.                 if k == keys.delete and i < #t then
  454.                     t = t:sub(1,i) .. t:sub(i+2,#t)
  455.                     break
  456.                 end
  457.                 if k == keys.backspace and i > 0 then
  458.                     t = t:sub(1,i-1) .. t:sub(i+1, #t)
  459.                     i = i - 1
  460.                     break
  461.                 end
  462.             end
  463.         until true
  464.     end
  465. end
  466. --custom dialoges (just the GUI, it also draws it)
  467. local function drawInfobox(txt) --just info text, no buttons
  468.     surf:fillRect(math.floor(w/2-(#txt+2)/2), math.floor(h/2)-2, math.floor(w/2+(#txt+2)/2),math.floor(h/2)+2, " ", lat_theme.menu_back, lat_theme.menu_text)
  469.     surf:drawText(math.floor(w/2-(#txt+2)/2)+1,math.floor(h/2)-1, txt, lat_theme.menu_back, lat_theme.menu_text)
  470.     surf:render()
  471. end
  472. local function drawTextbox(txt) --text feedback
  473.     surf:fillRect(math.floor(w/2-(#txt+2)/2),math.floor(h/2)-2,math.floor(w/2+(#txt+2)/2),math.floor(h/2)+2, " ", lat_theme.menu_back, lat_theme.menu_text)
  474.     surf:drawText(math.floor(w/2-(#txt+2)/2)+1,math.floor(h/2)-1, txt, lat_theme.menu_back, lat_theme.menu_text)
  475.     surf:render()
  476.     term.setCursorPos(math.floor(w/2-(#txt+2)/2)+1,math.floor(h/2)+1)
  477. end
  478. local function drawButtonBox(txt,buttons) --multiple buttons, configurable
  479.     if type(buttons) ~= "table" then
  480.         buttons = {{" Yes ",colors.red,colors.white},{" No ",colors.gray,colors.white}}
  481.     end
  482.     if txt == "" or txt == nil then
  483.         txt = " Are you sure? "
  484.     end
  485.    
  486.     drawTextbox(txt) --reuse the rectangle
  487.     local x,y = term.getCursorPos()
  488.    
  489.     for i=1,#buttons do
  490.         x = math.floor((w/2-(#txt+2)/2)+((#txt+2)/i)*(i-1)) + 1
  491.         if term.isColor() then
  492.             surf:drawText(x,y, buttons[i][1], buttons[i][2], buttons[i][3])
  493.         else
  494.             surf:drawText(x,y,buttons[i][1], lat_theme.main_back, lat_theme.main_text)
  495.         end
  496.     end
  497.     surf:render()
  498.     return y
  499. end
  500. local function drawPopupBox(x,y,buttons) --x and y are the corners
  501.     local ydir = y < h/2 and 1 or -1
  502.     surf:fillRect(x,y,x+15,y+(#buttons+1)*ydir, " ", lat_theme.menu_back, lat_theme.menu_text)
  503.     for k,v in pairs(buttons) do
  504.         surf:drawText(x+1,y+k*ydir, v, lat_theme.menu_back, lat_theme.menu_text)
  505.     end
  506.     surf:render()
  507. end
  508. --custom dialogue functionality (difference from popups is that they stop the main program flow, program cannot close, etc. they are allowed to handle events)
  509. local function infoBox(txt, noredraw)
  510.     drawInfobox(txt)
  511.     waitForKeyOrClick()
  512.     if not noredraw then
  513.         redraw()
  514.     end
  515. end
  516. local function textBox(txt, dtext)
  517.     drawTextbox(txt)
  518.     local resp = extensionRead("", #txt, lat_theme.menu_back, lat_theme.menu_text, dtext)
  519.     redraw()
  520.     return resp
  521. end
  522. local function xtensionTextBox(txt, xts)
  523.     drawTextbox(txt)
  524.     local resp = extensionRead(xts, #txt, lat_theme.menu_back, lat_theme.menu_text)
  525.     redraw()
  526.     return resp
  527. end
  528. local function fileChooseBox(txt)
  529.     return textBox(txt)
  530. end
  531. local function dirChooseBox(txt)
  532.     return textBox(txt)
  533. end
  534. local function buttonBox(txt, buttons)
  535.     local y = drawButtonBox(txt, buttons)
  536.     while true do
  537.         local _,b,x2,y2 = os.pullEvent("mouse_click")
  538.         if b == 1 and y == y2 then
  539.             for i=1,#buttons do
  540.                 local x = math.floor((w/2-(#txt+2)/2)+((#txt+2)/i)*(i-1)) + 1
  541.                 if x2 > x - 1 and x2 < x + #buttons[i][1] then
  542.                     redraw()
  543.                     return i
  544.                 end
  545.             end
  546.         end
  547.     end
  548. end
  549. --these were moved above popupbox:
  550. local function safeRun(func, ...)
  551.     local succ, msg = pcall(func, ...)
  552.     if not succ then
  553.         infoBox(msg)
  554.     end
  555. end
  556. local function refresh()
  557.     if pathValid then
  558.         readFolderRaw(path)
  559.     else
  560.         local t = {}
  561.         for k,v in pairs(itemPaths) do
  562.             if fs.exists(v) then
  563.                 t[#t+1] = v
  564.             end
  565.         end
  566.         itemPaths = t
  567.     end
  568. end
  569. local function popupBox(x,y,buttons,functions) --popups defined to have width 15
  570.     drawPopupBox(x,y,buttons)
  571.     local ydir = y < h/2 and 1 or -1
  572.     while true do
  573.         local _,b,cx,cy = os.pullEvent("mouse_click")
  574.         if b == 1 then
  575.             if cx < x or cx > x + 15 then
  576.                 os.queueEvent("mouse_click", b, cx, cy)
  577.                 break
  578.             end
  579.             if not (cy*ydir > y*ydir and cy*ydir - y*ydir < #buttons+1) then
  580.                 os.queueEvent("mouse_click", b, cx, cy)
  581.                 break
  582.             end
  583.             --for menus inside popup boxes
  584.             redraw()
  585.             safeRun(functions[ydir*cy-ydir*y],x,y)
  586.             refresh()
  587.             break
  588.         else
  589.             os.queueEvent("mouse_click", b, cx, cy)
  590.             break
  591.         end
  592.     end
  593.    
  594. end
  595.  
  596. local findFileBox --for later
  597. local findDirBox
  598.  
  599. --file editing functionality
  600. local function run(path, ...)
  601.     local tArgs = {...}
  602.     local function box()
  603.         clear()
  604.        
  605.         shell.run(fs.combine("/", path), unpack(tArgs)) --better alternative to shell.run?
  606.         print("Press any key or click to return to Lattix")
  607.         waitForKeyOrClick()
  608.     end
  609.     local cor = coroutine.create(box)
  610.     coroutine.resume(cor)
  611.     --later better sandboxing required that doesn't allow modification of _G or some file defense
  612.     while (coroutine.status(cor) ~= "dead") do
  613.         local event = {os.pullEventRaw()} --terminate works only on the sub-program
  614.         coroutine.resume(cor, unpack(event))
  615.     end
  616. end
  617. local function mkdir(path, name)
  618.     fs.makeDir(fs.combine(path, name))
  619. end
  620. local function paste(path, clipboard, cutEnabled) --i wonder what happens if you try to move something inside of itself
  621.     if type(clipboard) == "table" then
  622.         for i=1,#clipboard do
  623.             paste(path, clipboard[i], cutEnabled)
  624.         end
  625.         return
  626.     end
  627.     if not clipboard then error("Clipboard is empty",0) end
  628.     if not fs.exists(clipboard) then error("File copied doesn't exist",0) end
  629.     local func = cutEnabled and fs.move or fs.copy
  630.     local goal = fs.combine(path, fs.getName(clipboard))
  631.     local i = 1
  632.     while fs.exists(goal) do
  633.         goal = fs.combine(path, "Copy" .. (i>1 and tostring(i) or "") .. " of " .. fs.getName(clipboard))
  634.         i = i + 1
  635.     end
  636.     func(clipboard, goal)
  637. end
  638. local function advFind(path, wildcard, results)
  639.     if not results then results = {} end
  640.     local t = fs.find(fs.combine(path, wildcard))
  641.     for i=1,#t do
  642.         results[#results+1] = t[i]
  643.     end
  644.     local dirs = fs.list(path)
  645.     for i=1,#dirs do
  646.         if fs.isDir(fs.combine(path, dirs[i])) then
  647.             results = advFind(fs.combine(path, dirs[i]), wildcard, results)
  648.         end
  649.     end
  650.    
  651.     return results
  652. end
  653. --GUI core functionality
  654.  
  655. local function doubleClick(path) --contains a reference to refresh
  656.     selected = 0
  657.     if fs.isDir(path) then
  658.         switchFolder(path)
  659.     else
  660.         local elements = split(fs.getName(path), ".")
  661.         if #elements == 1 then
  662.             elements[2] = "" --no extension
  663.         end
  664.         if not defaultPrograms[elements[#elements]] then
  665.             elements[#elements] = "def" --unknown extension
  666.         end
  667.         if (defaultPrograms[elements[#elements]]) == "api" then
  668.             os.loadAPI(path)
  669.             refresh()
  670.         elseif defaultPrograms[elements[#elements]] == "run" then
  671.             run(path)
  672.             refresh()
  673.         else
  674.             shell.run(defaultPrograms[elements[#elements]], path)
  675.             refresh()
  676.         end
  677.     end
  678. end
  679. local function gotoFolder()
  680.     local target
  681.         target = textBox("Please specify target folder")
  682.         if target == "" then return end --cancel if empty
  683.         if not target or not fs.exists(target) or not fs.isDir(target) then
  684.             infoBox("    Not a valid directory   ")
  685.             return
  686.         end
  687.         switchFolder(fs.combine("", target))
  688. end
  689. local function makeDirPrompt()
  690.     local name = textBox("Name new directory")
  691.     if name == "" then return end
  692.     if fs.exists(fs.combine(path, name)) then
  693.         infoBox("Failure - File name already used")
  694.     end
  695.     mkdir(path, name)
  696. end
  697. local function pastebin()
  698.     local link = textBox("Enter the pastebin link")
  699.     if link == "" then return end
  700.     local name = textBox("Name")
  701.     if name == "" then return end
  702.     if fs.exists(fs.combine(path,name)) then
  703.         infoBox("Failure - File name already used")
  704.         return
  705.     end
  706.     pastebinGet(link, fs.combine(path, name))
  707. end
  708. local function wgetPrompt()
  709.     local link = textBox("Enter the url")
  710.     if link == "" then return end
  711.     local name = textBox("Name")
  712.     if name == "" then return end
  713.     if fs.exists(fs.combine(path,name)) then
  714.         infoBox("Failure - File name already used")
  715.         return
  716.     end
  717.     wGet(link, fs.combine(path, name))
  718. end
  719. local function runPrompt(appath)
  720.     if not appath then
  721.         appath = fs.combine(path, textBox("Name script"))
  722.     end
  723.     if not fs.exists(appath) then
  724.         infoBox("Script doesn't exist: " .. appath)
  725.         return
  726.     end
  727.     if fs.isDir(appath) then
  728.         infoBox("Cannot run a directory: " .. appath)
  729.         return
  730.     end
  731.     args = textBox("Arguments")
  732.     run(appath, split(args, " "))
  733. end
  734. local function copy(name)--name actually means full path
  735.     clip_cut = false
  736.     clipboard = name
  737. end
  738. local function cut(name) --name actually means full path here
  739.     copy(name)
  740.     clip_cut = true
  741. end
  742. local function renamePrompt(path)
  743.     if not fs.exists(path) then infoBox("Nothing to rename") return end
  744.     local name = textBox("Enter the new name", fs.getName(path))
  745.     if name == "" then return end
  746.     if fs.exists(fs.combine(fs.getDir(path), name)) then
  747.         infoBox("Failure - File already exists")
  748.         return
  749.     end
  750.     fs.move(path, fs.combine(fs.getDir(path), name))
  751. end
  752. local function deletePrompt(path)
  753.     if not fs.exists(path) then infoBox("Nothing to delete") return end
  754.     local response = buttonBox("Delete  ".. fs.getName(path) .. "?",
  755.     {{" Delete ", colors.white, colors.red}, {" Cancel ", colors.yellow, colors.black }})
  756.     if response == 1 then
  757.         fs.delete(path)
  758.     end
  759. end
  760. local function findFilesPrompt()
  761.     local wildCard = textBox("Enter filename or part of it")
  762.     if wildCard == "" then return end --cancel option
  763.     local finds = advFind(path, "*" .. wildCard .. "*")
  764.     if #finds == 0 then
  765.         infoBox("No files found")
  766.         return
  767.     end
  768.     itemNames = {}
  769.     itemPaths = {}
  770.     for i=1,#finds do
  771.         itemNames[i] = finds[i]
  772.         itemPaths[i] = finds[i]
  773.         customTitle = "Search results"
  774.         pathValid = false
  775.     end
  776.    
  777. end
  778. --GUI functionality - event mapping
  779. local buttons = {}
  780. local keymap = {}
  781. local eventmap = {}
  782.  
  783. for i=1,w do
  784.     buttons[i] = {}
  785.     for j=1,h do
  786.    
  787.         buttons[i][j] = {function() end, function() end}
  788.     end
  789. end
  790. local function newButton(x, y, w, h, func_left, func_right)
  791.     for i=x, x+w-1 do
  792.         for j=y, y+h-1 do
  793.             buttons[i][j] = {func_left, func_right}
  794.         end
  795.     end
  796. end
  797. local function clearButtons(x, y, w, h)
  798.     newButton(x,y,w,h, empty, empty)
  799. end
  800.  
  801. local function clearAllEvents()
  802.     keymap = {}
  803.     eventmap = {}
  804.     clearButtons(1,1,w,h)
  805. end
  806. local popup_newmenu_names = {
  807.     "New ...",
  808.     "New dir",
  809.     "Paste",
  810.     "Pastebin",
  811.     "wget"
  812. }
  813. local popup_newmenu_functions = {
  814.     function(x,y)
  815.         local options = {}
  816.         local functions = {}
  817.         for k,v in pairs(editors) do
  818.             local i = #options+1
  819.             options[i] = k
  820.             functions[i] = function()
  821.                 local ext = ""
  822.                 if editors_extensions[k] then
  823.                     ext = editors_extensions[k]
  824.                 end
  825.                 local app
  826.                 if v == "choose" then
  827.                    
  828.                 else
  829.                     app = v
  830.                 end
  831.                 local item = xtensionTextBox("What should the new file be called?", "." .. ext)
  832.                 local target = fs.combine(path, item)
  833.                 if not fs.exists(target) then
  834.                     shell.run(app, target)
  835.                 else
  836.                     infoBox("Failure - file already exists")
  837.                 end
  838.             end
  839.         end
  840.         popupBox(x,y,options,functions)
  841.     end,
  842.     function() makeDirPrompt() end,
  843.     function() paste(path, clipboard, clip_cut) end,
  844.     function() pastebin() end,
  845.     function() wgetPrompt() end,
  846.    
  847. }
  848. local popup_lockednewmenu_names = {
  849.     "Refresh"
  850. }
  851. local popup_lockednewmenu_functions = {
  852.     function() refresh() end
  853. }
  854.  
  855. local popup_menu_names = {
  856.     "Go to dir",
  857.     "Find file",
  858.     "Version: "..version
  859. }
  860. local popup_menu_functions = {
  861.     function() gotoFolder() end,
  862.     function() findFilesPrompt() end,
  863.     function() infoBox("Lattix version " .. version) end,
  864. }
  865. local filePopupNames = {
  866.     "Run", --runs
  867.     "Run w/ args", --runs with args
  868. --  "Open With", --select a program from a list and use it to open this, config file will be huge i see
  869.     "Rename",
  870.     "Copy",
  871.     "Cut",
  872.     "Delete"
  873. }
  874. local function getSelectedPath()
  875.     return itemPaths[selected]
  876. end
  877. local filePopupFunctions = {
  878.     function(x,y) run(getSelectedPath()) end,--run
  879.     function(x,y) runPrompt(getSelectedPath()) end,--run w args
  880.     function(x,y) renamePrompt(getSelectedPath()) end, --rename,
  881.     function(x,y) copy(getSelectedPath()) end, --copy
  882.     function(x,y) cut (getSelectedPath()) end, --cut
  883.     function(x,y) deletePrompt(getSelectedPath()) end --delete
  884. }
  885. local folderPopupNames = {
  886.     "Open",
  887. --  "Open in ...", --1,2,3,4 and program, TODO later bc open in and open with need some design decisions
  888. --  "Unpack",
  889.     "Rename",
  890.     "Copy",
  891.     "Cut",
  892.     "Delete"
  893. }
  894. local folderPopupFunctions = {
  895.     function(x,y) switchFolder(getSelectedPath()) end, --open
  896.     function(x,y) renamePrompt(getSelectedPath()) end, --rename
  897.     function(x,y) copy(getSelectedPath()) end, --copy
  898.     function(x,y) cut(getSelectedPath()) end, --cut
  899.     function(x,y) deletePrompt(getSelectedPath()) end --delete
  900. }
  901. local multiPopupNames = {}
  902. local multiPopupFunctions = {}--for multiselect, copy, cut, pack into folder, delete
  903.  
  904. local function mapMenu()
  905.     local back = function()
  906.         if not pathValid then
  907.             pathValid = true
  908.             refresh()
  909.            
  910.             return
  911.         end
  912.         if #history > 1 then
  913.             switchFolderRaw(history[#history])
  914.             table.remove(history, #history)
  915.         end
  916.     end
  917.     local up = function()
  918.         if not pathValid then
  919.             pathValid = true
  920.             refresh()
  921.             return
  922.         end
  923.         if path == "" or path == "/" then return end
  924.         switchFolder(fs.combine(path, ".."))
  925.     end
  926.     local menu = function()
  927.         --open advanced menu
  928.         popupBox(9,2,popup_menu_names, popup_menu_functions)
  929.     end
  930.     local root = function()
  931.         if not pathValid then
  932.             pathValid = true
  933.         end
  934.         switchFolder(root)
  935.     end
  936.     local plus = function()
  937.         --open new menu
  938.         if pathValid then
  939.             popupBox(5,2,popup_newmenu_names, popup_newmenu_functions)
  940.         else
  941.             popupBox(5,2,popup_lockednewmenu_names, popup_lockednewmenu_functions)
  942.         end
  943.     end
  944.     local quit = function()
  945.         clear()
  946.         endprogram = true
  947.     end
  948.     local switch = function(e)
  949.         switchSessions(e[3]-w+6)
  950.     end
  951.     newButton(1,1,1,1, back, empty)
  952.     newButton(3,1,1,1, up, empty)
  953.     newButton(7,1,1,1, root, empty)
  954.     newButton(5,1,1,1, plus, empty)
  955.     newButton(9,1,1,1, menu, empty)
  956.     newButton(w,1,1,1, quit, empty)
  957.     newButton(w-5,1,4,1, switch, empty)
  958.     keymap[keys.left] = up
  959. end
  960. local drawFiles
  961.  
  962. local function enterPress()
  963.     words = split(shell_input, " ")
  964.     if words and #words > 0 and commands[words[1]] then
  965.         commands[words[1]](words)
  966.     elseif selected > 0 and selected <= #itemPaths then
  967.         doubleClick(itemPaths[selected])
  968.     end
  969.     shell_input = ""
  970. end
  971. local function filePopup(x,y,path)
  972.     if fs.isDir(path) then
  973.         --directory
  974.         popupBox(x,y,folderPopupNames, folderPopupFunctions)
  975.     else
  976.         --file
  977.         popupBox(x,y,filePopupNames, filePopupFunctions)
  978.     end
  979. end
  980. local function mapFiles()
  981.     --popup menu implementation (right click)
  982.     local file_rightclick = function(e)
  983.         local i = posToIndex(#itemNames,e[3],e[4],scroll) --get index
  984.         if itemPaths[i] then
  985.             if selected ~= i then --select if not selected
  986.                 selected = i --just for aesthetics
  987.                 redraw()
  988.             end
  989.             --show file/folder/multiselect relevant popup
  990.             if selection and #selection > 0 then
  991.                 --multiselect
  992.             else
  993.                 filePopup(e[3],e[4],getSelectedPath())
  994.             end
  995.         else
  996.             selected = 0
  997.             --show the same popup as the + button in the menu
  998.             if pathValid then
  999.                 popupBox(e[3],e[4],popup_newmenu_names, popup_newmenu_functions)
  1000.             else
  1001.                 popupBox(e[3], e[4], popup_lockednewmenu_names, popup_lockednewmenu_functions)
  1002.             end
  1003.         end
  1004.     end
  1005.     --select implementation (left click)
  1006.     local file_leftclick = function(e)
  1007.         local i = posToIndex(#itemNames,e[3],e[4],scroll)
  1008.         if itemPaths[i] then
  1009.             if selected == i then
  1010.                 doubleClick(itemPaths[i])
  1011.             else
  1012.                 selected = i
  1013.             end
  1014.         else
  1015.             selected = 0
  1016.         end
  1017.     end
  1018.    
  1019.     newButton(1,2,w,h-2,file_leftclick, file_rightclick)
  1020.     --multiselect stuff
  1021.    
  1022.     --scrolling
  1023.     eventmap["mouse_scroll"] = function(e)
  1024.         local b = e[2]
  1025.         if b == 1 and #itemPaths - scroll > h-2 then
  1026.             scroll = scroll + 1
  1027.         end
  1028.         if b == -1 and scroll > 0 then
  1029.             scroll = scroll - 1
  1030.         end
  1031.     end
  1032.    
  1033.     keymap[keys.enter] = enterPress
  1034.     keymap[keys.up] = function()
  1035.         if selected > 1 then
  1036.             selected = selected - 1
  1037.             if scroll >= selected then
  1038.                 scroll = selected - 1
  1039.             end
  1040.         end
  1041.         if selected == 0 then
  1042.             selected = #itemPaths
  1043.             if scroll + h - 2 < selected then
  1044.                 scroll = selected - h + 2
  1045.             end
  1046.         end
  1047.     end
  1048.     keymap[keys.down] = function()
  1049.         if selected < #itemPaths then
  1050.             selected = selected + 1
  1051.             if scroll + h - 2 < selected then
  1052.                 scroll = selected - h + 2
  1053.             end
  1054.         end
  1055.     end
  1056.     keymap[keys.right] = function()
  1057.         if selected > 0 and selected <= #itemPaths then
  1058.             local x,y = IndexToPos(selected, scroll)
  1059.             if x < 2 then
  1060.                 x = 2
  1061.             end
  1062.             if x > h-1 then
  1063.                 x = h-1
  1064.             end
  1065.             filePopup(x,y, getSelectedPath())
  1066.         end
  1067.     end
  1068. end
  1069. local commands = { --table of functions, arg: list of words typed, including the command itself
  1070.     ["run"] = function(words)
  1071.         if #words == 1 then
  1072.             runPrompt()
  1073.             return
  1074.         end
  1075.         local path = fs.combine(path, words[2])
  1076.         local args = {}
  1077.         if #words > 2 then
  1078.             for i=3, #words do
  1079.                 args[i-2] = words[i]
  1080.             end
  1081.         end
  1082.         if fs.exists(path) and not fs.isDir(path) then
  1083.             run(path, unpack(args))
  1084.         end
  1085.     end,
  1086.     ["goto"] = function(words)
  1087.         local target
  1088.         if #words < 2 then
  1089.             gotoFolder()
  1090.             return
  1091.         else
  1092.             target = words[2]
  1093.         end
  1094.         if not target or not fs.exists(target) or not fs.isDir(target) then
  1095.             infoBox("    Not a valid directory   ")
  1096.             return
  1097.         end
  1098.         switchFolder(fs.combine("", target))
  1099.     end
  1100. }
  1101. local function mapBar()
  1102.     eventmap.char = function(e)
  1103.         shell_input = shell_input .. e[2]
  1104.     end
  1105.     keymap[keys.backspace] = function()
  1106.         shell_input = shell_input:sub(1, #shell_input-1)
  1107.     end
  1108.     --enter is mapped in mapFiles for the bar too
  1109. end
  1110. --draw components
  1111. local function drawMenu()
  1112.     surf:drawLine(1,1,w,1," ", lat_theme.menu_back, lat_theme.menu_text)
  1113.     if term.isColor() then
  1114.         surf:drawText(1,1,"< ^ + / m", lat_theme.menu_back, lat_theme.menu_text)
  1115.     end
  1116.     local str
  1117.     if customTitle then
  1118.         str = customTitle
  1119.     else
  1120.         if path ~= "" then
  1121.             str = cSession .. " - " .. path
  1122.         else
  1123.             str = tostring(cSession)
  1124.         end
  1125.     end
  1126.     str = #str < w/2 and str or str:sub(1,math.floor(w/2)) .. "..."
  1127.     surf:drawText(8+math.floor((w-12)/2-#str/2),1,str, lat_theme.menu_back, lat_theme.menu_text)
  1128.    
  1129.     if term.isColor() then
  1130.         surf:drawText(w-5,1,"1234", lat_theme.menu_back, lat_theme.menu_text)
  1131.         surf:drawPixel(w,1,"x", colors.red, colors.white)
  1132.     end
  1133.    
  1134. end
  1135.  
  1136. drawFiles = function()
  1137.     if selection == nil then selection = {} end --just in case selection was nilled
  1138.     local cy = 2 --current y pos of drawing
  1139.     local i = scroll + 1 --index in "items"
  1140.     local tcol
  1141.     local bcol
  1142.     while IndexToPos(i,scroll) < h do
  1143.         if not itemNames[i] then break end --because the while condition checks for screen size, this checks for file count
  1144.         local twrite = ""
  1145.         bcol = (lat_theme.main_back)
  1146.         if selection[i] then
  1147.             bcol = (lat_theme.multiselect_back)
  1148.             twrite = twrite .. selectprefix
  1149.         end
  1150.         if i ~= selected then
  1151.             if not fs.isDir(itemPaths[i]) then
  1152.                 tcol = (lat_theme.main_text)
  1153.             else
  1154.                 tcol = (lat_theme.dir_text)
  1155.                
  1156.             end
  1157.         else
  1158.             tcol = (lat_theme.selected_text)
  1159.         end
  1160.         if fs.isDir(itemPaths[i]) then
  1161.             twrite = twrite .. dirprefix
  1162.         end
  1163.         twrite = twrite .. itemNames[i]
  1164.         surf:drawLine(1, cy, w, cy, " ", bcol, tcol)
  1165.         surf:drawText(2, cy, twrite, bcol, tcol)
  1166.         local mwrite = "-"
  1167.         if not fs.isDir(itemPaths[i]) then
  1168.             mwrite = formatNumber(fs.getSize(itemPaths[i]))
  1169.         end
  1170.         local startX = w-6
  1171.         surf:drawLine(startX-1, cy, w, cy, " ", bcol, tcol)
  1172.         surf:drawText(startX,cy, mwrite, bcol, tcol)
  1173.         i = i + 1
  1174.         cy = cy + 1 --up both indexes
  1175.     end
  1176.     return itemNames
  1177. end
  1178.  
  1179. local function drawBar()
  1180.     surf:drawLine(1,h,w,h, " ", lat_theme.menu_back, lat_theme.menu_text)
  1181.     surf:drawText(1,h, path .. "> " .. shell_input, lat_theme.menu_back, lat_theme.menu_text)
  1182. end
  1183.  
  1184.  
  1185. --GUI drawing functions
  1186. redraw = function()
  1187.     if not fs.exists(path) or not fs.isDir(path) then
  1188.         infoBox("Error: " .. path .. " is not a valid directory", true)
  1189.         path = root
  1190.     end
  1191.    
  1192.     surf:clear(" ", lat_theme.blank_back, lat_theme.blank_text)
  1193.     drawMenu()
  1194.    
  1195.     drawFiles()
  1196.    
  1197.     drawBar()
  1198.    
  1199.     surf:render()
  1200. end
  1201.  
  1202. remap = function()
  1203.     clearAllEvents()
  1204.     mapMenu()
  1205.     mapFiles()
  1206.     mapBar()
  1207. end
  1208. local oRedraw = redraw --orginal backup
  1209. local oRemap = remap
  1210. restore = function()
  1211.     redraw = oRedraw
  1212.     remap = oRemap
  1213. end
  1214.  
  1215. do --choose file/folder dialog
  1216.  
  1217. end
  1218.  
  1219. switchFolder(path)
  1220.  
  1221. --main loop
  1222. while not endprogram do
  1223.     redraw()
  1224.     remap()
  1225.     local e = { os.pullEvent() }
  1226.     if e[1] == "mouse_click" then
  1227.         buttons[e[3]][e[4]][e[2]](e)
  1228.     elseif e[1] == "key" then
  1229.         if keymap[e[2]] then
  1230.             keymap[e[2]](e)
  1231.         end
  1232.     elseif eventmap[e[1]] then
  1233.         eventmap[e[1]](e)
  1234.     end
  1235. end
  1236.  
  1237. end
  1238.  
  1239. local succ, msg = pcall(program)
  1240.  
  1241. term.setBackgroundColor(colors.black)
  1242. term.setTextColor(colors.white)
  1243. term.clear()
  1244. term.setCursorPos(1,1)
  1245.    
  1246. if not succ and msg ~= "Terminated" then
  1247.     print("Congratulations, Lattix has crashed")
  1248.     print()
  1249.     print("Please report with steps to reproduce to the forum post to get your name added to the credits")
  1250.     print()
  1251.     print(msg)
  1252. end
  1253. if succ or msg == "Terminated" then
  1254.     print("[Lattix]: bye")
  1255.     print()
  1256.     print(msg or "version " .. version)
  1257. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement