ClientOS/LevelOS/desktop.lua
2025-10-21 00:40:14 +02:00

1608 lines
43 KiB
Lua
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local terminal = term.current()
local deskIcons = lUtils.asset.load("LevelOS/assets/Desktop_Icons.limg")
local desktop = {}
local idReference = {}
local fc = fs.combine
local function locateEntry(tTable,tEntry)
if type(tTable) ~= "table" then
error("Invalid input #1",2)
end
for t=1,#tTable do
if tTable[t] == tEntry then
return true,t
end
end
return false,0
end
local fileTypes
if fs.exists("LevelOS/data/formats.lconf") then
fileTypes = lUtils.asset.load("LevelOS/data/formats.lconf")
end
if not fileTypes then
local copy = lUtils.instantiate
fileTypes = {
_VERSION="1.3",
[""] = {
name="File",
program="Lua",
contextMenu={
{"Execute","Edit"},
{"Lua","rom/programs/edit.lua"}
},
openWith={
"Lua",
"rom/programs/edit.lua"
},
icon=deskIcons[1]
},
lua = {
name="Lua script",
program="Lua",
contextMenu={
{"Execute","Edit"},
{"Lua","rom/programs/edit.lua"}
},
openWith={
"Lua",
"rom/programs/edit.lua"
},
icon=deskIcons[2],
emptyFilePreset=""
},
txt = {
name="Text file",
program="LevelOS/notepad.lua",
contextMenu={
{"Edit"},
{"LevelOS/notepad.lua"}
},
openWith={
"LevelOS/notepad.lua",
"rom/programs/edit.lua"
},
icon=deskIcons[5],
emptyFilePreset=""
},
lconf = {
name="Config file",
program="LevelOS/notepad.lua",
contextMenu={
{"Edit"},
{"LevelOS/notepad.lua"}
},
openWith={
"LevelOS/notepad.lua",
"rom/programs/edit.lua"
},
icon=deskIcons[6],
emptyFilePreset="{}"
},
sgui = {
name="Shapescape GUI",
program="Program_Files/Shapescape",
contextMenu={
{"Open","Execute"},
{"Program_Files/Shapescape","Lua"}
},
openWith={
"Lua",
"Program_Files/Shapescape",
"rom/programs/edit.lua"
},
icon=deskIcons[8],
emptyFilePreset="return {assets={},slides={{contextMenu=1,objs={},w=51,h=19}}}"
},
sml = {
name="SML Document",
program="Program_Files/sml",
contextMenu={
{"Open","Edit"},
{"Program_Files/sml","rom/programs/edit.lua"}
},
openWith={
"Program_Files/sml",
"rom/programs/edit.lua"
},
icon=deskIcons[9],
emptyFilePreset=""
},
limg = {
name="Image",
program="LevelOS/imageviewer.lua",
contextMenu={
{"View","Edit"},
{"LevelOS/imageviewer.lua","rom/programs/edit.lua"}
},
openWith={
"LevelOS/imageviewer.lua",
"rom/programs/edit.lua"
},
icon=copy(deskIcons[10])
},
bimg = {
name="Image",
program="LevelOS/imageviewer.lua",
contextMenu={
{"View","Edit"},
{"LevelOS/imageviewer.lua","rom/programs/edit.lua"}
},
openWith={
"LevelOS/imageviewer.lua",
"rom/programs/edit.lua"
},
icon=copy(deskIcons[10])
},
nfp = {
name="NFP Image",
program="LevelOS/imageviewer.lua",
contextMenu={
{"View","Edit"},
{"LevelOS/imageviewer.lua","rom/programs/fun/advanced/paint.lua"}
},
openWith={
"LevelOS/imageviewer.lua",
"rom/programs/fun/advanced/paint.lua",
"rom/programs/edit.lua"
},
icon=copy(deskIcons[10]),
emptyFilePreset=""
},
nfg = {
name="Animation",
program="LevelOS/imageviewer.lua",
contextMenu={
{"View","Edit"},
{"LevelOS/imageviewer.lua","rom/programs/edit.lua"}
},
openWith={
"LevelOS/imageviewer.lua",
"rom/programs/edit.lua"
},
icon=copy(deskIcons[10])
},
}
lUtils.asset.save(lUtils.instantiate(fileTypes),"LevelOS/data/formats.lconf", false)
end
if fileTypes._VERSION == "1.0" then
for k,v in pairs(fileTypes) do
if type(v) == "table" then
v.name = v.name or v.n
v.n = nil
v.program = v.program or v.p
v.p = nil
v.icon = v.icon or v.i
v.i = nil
v.contextMenu = v.contextMenu or v.c
v.c = nil
v.openWith = v.openWith or v.o
v.o = nil
v.emptyFilePreset = v.emptyFilePreset or v.e
end
end
fileTypes._VERSION = "1.1"
fileTypes["llnk"] = {name="Shortcut",program="Lua"}
lUtils.asset.save(lUtils.instantiate(fileTypes),"LevelOS/data/formats.lconf")
end
if fileTypes._VERSION == "1.1" then
for k,v in pairs(fileTypes) do
if type(v) == "table" then
v._VERSION = nil
end
end
for k,v in pairs(fileTypes.nfp.openWith) do
if v == "rom/programs/fun/paint.lua" then
fileTypes.nfp.openWith[k] = "rom/programs/fun/advanced/paint.lua"
end
end
fileTypes._VERSION = "1.2"
lUtils.asset.save(lUtils.instantiate(fileTypes),"LevelOS/data/formats.lconf",false)
end
if fileTypes._VERSION == "1.2" then
fileTypes.bimg = {
name="Image",
program="LevelOS/imageviewer.lua",
contextMenu={
{"View","Edit"},
{"LevelOS/imageviewer.lua","rom/programs/edit.lua"}
},
openWith={
"LevelOS/imageviewer.lua",
"rom/programs/edit.lua"
},
icon=deskIcons[10]
}
fileTypes._VERSION = "1.3"
lUtils.asset.save(lUtils.instantiate(fileTypes),"LevelOS/data/formats.lconf",false)
end
local dConfig
if not lOS.explorer then lOS.explorer = {} end
lOS.explorer.shortcache = {}
lOS.explorer.imgcache = {}
lOS.explorer.nimgcache = {}
lOS.explorer.desktopBackground = nil
lOS.fClipboard = {}
local disks = {}
lOS.explorer.disks = disks
local function reloadDisks()
for k,v in pairs(disks) do
disks[k] = nil
end
local ndisks = {peripheral.find("drive",function(name,object) return object.hasData() end)}
for t=1,#ndisks do
ndisks[ndisks[t].getMountPath()] = ndisks[t]
end
for k,v in pairs(ndisks) do
disks[k] = v
end
end
reloadDisks()
local function getName(path)
local isDisk = false
if disks[path] and disks[path].getDiskLabel() then
str = disks[path].getDiskLabel().." ("..string.gsub(path,"_"," ")..")"
isDisk = true
elseif path == "" then
str = "Local disk"
else
str = string.gsub(fs.getName(path),"_"," ")
if lUtils.getFileType(str) == ".llnk" then
str = str:sub(1,#str-5)
end
end
return str,isDisk
end
lOS.explorer.getName = getName
local symbols = {
["User/Cloud"] = {symbol="",color=colors.blue},
[""] = {symbol="",color=colors.lightGray},
["rom"] = {symbol="",color=colors.red},
["User"] = {symbol="",color=colors.lightBlue},
["User/Desktop"] = {symbol="",color=colors.lightBlue},
["User/Documents"] = {symbol="",color=colors.lightGray},
["User/Downloads"] = {symbol="",color=colors.blue},
["User/Images"] = {symbol="",color=colors.lightBlue},
["User/Scripts"] = {symbol="",color=colors.cyan},
["User/Games"] = {symbol="",color=colors.lightGray},
["User/Music"] = {symbol="",color=colors.blue},
}
lOS.explorer.getIcon = function(path)
local disks = {peripheral.find("drive",function(name,object) return object.hasData() end)}
for t=1,#disks do
disks[disks[t].getMountPath()] = disks[t]
end
if symbols[path] then
return symbols[path].symbol, symbols[path].color
elseif disks[path] then
return "", colors.blue
elseif fs.isDir(path) then
return "\143", colors.yellow
else
return "\143", colors.white
end
end
local function genFile(f)
if not fs.exists(f) then
error(f.." does not exist",2)
end
local file = {path=f,name=fs.getName(f),type=lUtils.getFileType(f):sub(2),readOnly=fs.isReadOnly(f),isDir=fs.isDir(f)}
if not ((file.isDir and file.readOnly) or disks[file.path]) then
local a = fs.attributes(f)
for k,v in pairs(a) do
file[k] = v
end
end
return file
end
local function uFP(filepath2) -- unique filepath
if fs.exists(filepath2) == true then
t = 1
while fs.exists(string.sub(filepath2,1,string.len(filepath2)-string.len(lUtils.getFileType(filepath2))).."_("..t..")"..lUtils.getFileType(filepath2)) == true do
t = t+1
end
filepath2 = string.sub(filepath2,1,string.len(filepath2)-string.len(lUtils.getFileType(filepath2))).."_("..t..")"..lUtils.getFileType(filepath2)
end
return filepath2
end
lOS.explorer.uFP = uFP
function lOS.genIco(path)
local name = getName(path)
if fs.isDir(path) and fs.exists(fs.combine(path,"main.lua")) then
path = fs.combine(path,"main.lua")
end
local pth = fs.combine("User/Desktop",name..".llnk")
lUtils.asset.save({path},pth)
os.queueEvent("explorer_reload")
end
local presetIcons = {
["User"] = deskIcons[12],
["User/Desktop"] = deskIcons[13],
["User/Documents"] = deskIcons[14],
["User/Downloads"] = deskIcons[15],
["User/Games"] = deskIcons[16],
["User/Images"] = deskIcons[17],
["User/Music"] = deskIcons[18],
["User/Scripts"] = deskIcons[19],
["User/Cloud"] = deskIcons[20],
["rom"] = deskIcons[21],
}
function lOS.explorer.drawIcon(path,fx,fy,onlyIcon,transparent,maxLines,shortcutIcon)
--local file = {path=path,type=lUtils.getFileType(path):sub(2)}
local ok,file = pcall(genFile,path)
if not ok then
lUtils.renderImg(deskIcons[11],fx+2,fy+1,nil,transparent)
return
end
local bg = term.getBackgroundColor()
local fg = term.getTextColor()
local cTerm = term.current()
local isCut = false
if lOS.fCut then
isCut = locateEntry(lOS.fCut,file.path)
end
local shortcache = lOS.explorer.shortcache
local imgcache = lOS.explorer.imgcache
local nimgcache = lOS.explorer.nimgcache
local fc = fs.combine
local rPath = path
local rType = file.type
local isShort = false
local lines = lUtils.wordwrap(lUtils.getFileName(file.path),7)
local lineH = math.min(#lines,4)
local fw,fh = 7,5
if #lines > lineH then
lines[4] = lines[4]:sub(1,5)..".."
end
fh = math.max(4+lineH,fh)
if not transparent then
lOS.boxClear(fx,fy,fx+7-1,fy+5-1+lineH-1)
end
if shortcache[file.path] then
isShort = true
rPath = shortcache[file.path][1]
rType = lUtils.getFileType(rPath):sub(2)
elseif file.type == "llnk" then
local info = lUtils.asset.load(file.path)
if info and type(info[1]) == "string" then
isShort = true
rPath = info[1]
if not fs.exists(rPath) then
imgcache[file.path] = {}
for i=0,15 do
imgcache[file.path][i] = deskIcons[4]
end
else
rType = lUtils.getFileType(info[1]):sub(2)
imgcache[file.path] = {}
for i=0,15 do
imgcache[file.path][i] = info.icon
end
end
shortcache[file.path] = info
end
end
local pFolder = fs.getDir(rPath)
if not isCut then
if not fs.exists(rPath) then
lUtils.renderImg(deskIcons[4],fx+2,fy+1,nil,transparent)
elseif presetIcons[rPath] then
lUtils.renderImg(presetIcons[rPath],fx+2,fy+1,nil,transparent)
elseif imgcache[file.path] and imgcache[file.path][bg] then
local win = imgcache[file.path][bg]
if win.reposition then
for t=1,3 do
term.setCursorPos(fx+2,fy+t)
term.blit(win.getLine(t))
end
else
lUtils.renderImg(win,fx+2,fy+1,nil,transparent)
end
elseif fs.getName(rPath) == "main.lua" --[[and fs.exists(fc(pFolder,"icon.limg"))]] then
if not nimgcache[file.path] and (fs.exists(fc(pFolder,"icon.limg")) or fs.exists(fc(pFolder,"icon.bimg"))) then
local img
if fs.exists(fc(pFolder,"icon.limg")) then
img = lUtils.asset.load(fc(pFolder,"icon.limg"))
else
img = lUtils.asset.load(fc(pFolder,"icon.bimg"))
end
img = img[1]
local iw = #img[1][1]
local ih = #img
if iw <= 3 and ih <= 3 then
--[[local tWin = window.create(cTerm,fx+2,fy+1,iw,ih,true)
term.redirect(tWin)
lUtils.renderImg(img,1,1)
term.redirect(cTerm)
imgcache[file.path] = tWin]]
if not imgcache[file.path] then
imgcache[file.path] = {}
end
imgcache[file.path][bg] = img
lUtils.renderImg(img,fx+2,fy+1,nil,transparent)
else
nimgcache[file.path] = true
end
else
lUtils.renderImg(deskIcons[3],fx+2,fy+1,nil,transparent)
end
elseif fs.isDir(rPath) then
if not nimgcache[file.path] and (fs.exists(fc(rPath,"icon.limg")) or fs.exists(fc(rPath,"icon.bimg"))) and fs.exists(fc(rPath,"main.lua")) then
local img
if fs.exists(fc(rPath,"icon.limg")) then
img = lUtils.asset.load(fc(rPath,"icon.limg"))
else
img = lUtils.asset.load(fc(rPath,"icon.bimg"))
end
img = img[1]
local iw = #img[1][1]
local ih = #img
if iw <= 3 and ih <= 3 then
local tWin = window.create(cTerm,fx+2,fy+1,iw,ih,true)
term.redirect(tWin)
if transparent then
for y=1,2 do
local line = {cTerm.getLine(fy+y)}
for t=1,3 do
line[t] = line[t]:sub(fx+2,fx+4)
end
term.setCursorPos(1,y)
term.blit(unpack(line))
end
else
term.setBackgroundColor(bg)
term.clear()
end
lUtils.renderImg(img,1,1,nil,transparent)
term.setBackgroundColor(colors.yellow)
term.setCursorPos(1,3)
term.write(" ")
term.redirect(cTerm)
if not imgcache[file.path] then
imgcache[file.path] = {}
end
imgcache[file.path][bg] = tWin
else
nimgcache[file.path] = true
end
else
lUtils.renderImg(deskIcons[7],fx+2,fy+1,nil,transparent)
end
elseif ((rType == "limg" or file.type == "bimg") and lUtils.asset.load(rPath)) or rType == "nfp" then
local iw,ih
local img
if rType == "nfp" then
img = lUtils.fread(rPath)
ih = 0
iw = 0
for line in img:gmatch("([^\n]*)\n?") do
iw = math.max(iw,#line)
ih = ih+1
end
else
img = lUtils.asset.load(rPath)
img = img[1]
iw = #img[1][1]
ih = #img
end
local tWin = window.create(cTerm,1,1,iw,ih,false)
term.redirect(tWin)
term.setBackgroundColor(bg)
term.clear()
lUtils.renderImg(img,1,1)
term.redirect(cTerm)
if not imgcache[file.path] then
imgcache[file.path] = {}
end
if iw <= 3 and ih <= 3 then
imgcache[file.path][bg] = img
lUtils.renderImg(img,fx+2,fy+1,nil,transparent)
else
local lWin = lUtils.littlewin(tWin,6,math.floor(9*(ih/iw)+0.5))
local fWin = window.create(cTerm,fx+2,fy+1,3,3,true)
--_G.debuglwin[file.path] = {lWin=lWin,tWin=tWin,img=img}
term.redirect(fWin)
local win = blittle.createWindow(fWin,1,1,3,3,true)
term.redirect(win)
if transparent then
for y=1,3 do
local line = {cTerm.getLine(fy+y)}
for t=1,3 do
line[t] = line[t]:sub(fx+2,fx+4)
end
term.setCursorPos(1,y)
term.blit(unpack(line))
end
else
term.setBackgroundColor(bg)
term.clear()
end
lWin.render(1,1)
term.redirect(fWin)
win.redraw()
term.redirect(cTerm)
fWin.redraw()
imgcache[file.path][bg] = fWin
end
elseif fileTypes[rType] and fileTypes[rType].icon then
lUtils.renderImg(fileTypes[rType].icon,fx+2,fy+1,nil,transparent)
else
lUtils.renderImg(deskIcons[4],fx+2,fy+1,nil,transparent)
end
if shortcutIcon then
if isShort and not onlyIcon then
term.setCursorPos(fx+2,fy+3)
term.blit(unpack(shortcutIcon))
elseif lOS.cloud and lOS.cloud.lastSync and lOS.cloud.files and lOS.cloud.files[file.path] then
term.setCursorPos(fx+2,fy+3)
local pix = {lUtils.getPixel(term.current(),fx+2,fy+3)}
term.setBackgroundColor(lUtils.toColor(pix[3]))
if lOS.cloud.conflicts[file.path] then
term.setTextColor(colors.red)
term.write("×")
elseif file.modification > lOS.cloud.lastSync then
term.setTextColor(colors.blue)
term.write("\24")
else
term.setTextColor(colors.lime)
term.write("\7")
end
end
end
end
term.setBackgroundColor(bg)
term.setTextColor(fg)
if not onlyIcon then
if maxLines then
lineH = math.min(maxLines,lineH)
end
for l=1,lineH do
local line = lines[l]:gsub("%s+$","")
term.setCursorPos(fx+math.floor(fw/2-#line/2),fy+3+l)
if transparent then
lUtils.transWrite(line,(not dConfig.textColor))
else
term.write(line)
end
end
end
return fh
end
dConfig = {_VERSION=1,files={},sizes={},shortcutIcon=true,background={color=colors.white}}
if fs.exists("LevelOS/data/desktop.lconf") then
dConfig = lUtils.asset.load("LevelOS/data/desktop.lconf")
end
if not dConfig._VERSION then
-- backup n shit
if fs.exists("LevelOS/data/olddesktop.lconf") then
fs.delete("LevelOS/data/olddesktop.lconf")
end
fs.move("LevelOS/data/desktop.lconf","LevelOS/data/olddesktop.lconf")
dConfig = {_VERSION=1,files={},sizes={},shortcutIcon=true,background={color=colors.white}}
end
lOS.explorer.desktopConfig = dConfig
function lOS.save()
lUtils.asset.save(dConfig,"LevelOS/data/desktop.lconf")
lUtils.fwrite("LevelOS/data/settings.lconf",textutils.serialize(lOS.settings))
return true
end
if not fs.exists("User/Desktop") then
fs.makeDir("User/Desktop")
-- my PC
lUtils.asset.save(
{
"Program_Files/LevelOS/Explorer/main.lua",
icon = {
{
"—ƒ”",
"ff ",
" f",
},
{
"ƒŽ",
"f f",
" f ",
},
{
"‚ƒ",
"fff",
" ",
}
},
},
"User/Desktop/My_PC.llnk"
)
-- shell
lUtils.asset.save(
{
"rom/programs/shell.lua",
icon = {
{
"‡ƒ‹",
" ",
"fff",
},
{
"„",
"f44",
"4ff",
},
{
"‹‡",
"fff",
" ",
}
},
},
"User/Desktop/Shell.llnk"
)
local function makeShortcuts(folder)
local ls = fs.list(folder)
for i,f in ipairs(ls) do
local path = fc(folder,f)
if fs.isDir(path) then
if fs.exists(fc(path,"main.lua")) then
lUtils.asset.save({fc(path,"main.lua")},fc("User/Desktop",fs.getName(f))..".llnk")
else
makeShortcuts(path)
end
end
end
end
makeShortcuts("Program_Files")
end
-- dConfig should be loaded after this
local currentID = 1
if not dConfig.files then dConfig.files = {} end
for id,path in pairs(dConfig.files) do
if id >= currentID then
currentID = id+1
end
idReference[path] = id
end
local desktopMap = setmetatable({}, {
__index = function(self, id)
local default = {}
self[id] = default
return default
end
})
_G.debugdesktopmap = desktopMap
local function clearDesktopMap()
for k,v in pairs(desktopMap) do
desktopMap[k] = nil
end
end
local function getDesktopSize()
local w,h = term.getSize()
if lOS.tbSize then
h = h - lOS.tbSize
end
local deskW,deskH = math.floor((w+1)/8),math.floor(h/5)
return deskW,deskH
end
local currentSize
do
local desktopWidth,desktopHeight = getDesktopSize()
currentSize = desktopWidth..","..desktopHeight
end
if not dConfig.sizes[currentSize] then
dConfig.sizes[currentSize] = {}
end
local function reloadDesktop()
if not fs.exists("User/Desktop") then
fs.makeDir("User/Desktop")
end
local desktopFiles = fs.list("User/Desktop")
local changed = false
for f=1,#desktopFiles do
local name = desktopFiles[f]
if not idReference[name] then
idReference[name] = currentID
dConfig.files[currentID] = name
currentID = currentID+1
changed = true
end
end
if changed then
lUtils.asset.save(dConfig,"LevelOS/data/desktop.lconf")
end
-- i only need to store position, but if i store them based on names everything gets messed up if a file gets renamed...
-- iterate through IDs too to see if theres a file there that doesnt exist anymore wait nah just do in the thing actually wait hmm
end
reloadDesktop()
local function drawBackground(background)
for i=0,15 do
term.setPaletteColor(2^i,term.nativePaletteColor(2^i))
end
if not background then
term.setBackgroundColor(colors.white)
term.clear()
elseif background.path and fs.exists(background.path) then
local col = background.color or colors.white
local tw,th = term.getSize()
local rImg
local img
local w,h
local t = lUtils.getFileType(background.path)
if t == ".bimg" or t == ".limg" or t == ".nfg" then
img = lUtils.asset.load(background.path)
rImg = img[1]
w = #rImg[1][1]
h = #rImg
elseif t == ".nfp" then
rImg = lUtils.fread(background.path)
h = 0
w = 0
local emptyLine = false
for line in rImg:gmatch("([^\n]*)\n?") do
w = math.max(w,#line)
h = h+1
if line == "" then
emptyLine = true
else
emptyLine = false -- only if emptyLine is at end itll be true, very good
end
end
if emptyLine then -- fking craftos pc man
h = h-1
end
end
if background.resize == "stretch" then
local tWin = window.create(term.current(),1,1,w,h,false)
local oterm = term.current()
term.redirect(tWin)
term.setBackgroundColor(col)
term.clear()
lUtils.renderImg(rImg,1,1)
term.redirect(oterm)
if h/w > th/tw then -- height should overflow, width equals screen width
local iw,ih=tw,math.floor(tw*(h/w)+0.5)
local lWin = lUtils.littlewin(tWin,iw,ih)
lWin.render(1,math.floor(th/2-ih/2)+1)
else
local iw,ih=math.floor(th*(w/h)+0.5),th
local lWin = lUtils.littlewin(tWin,iw,ih)
lWin.render(math.floor(tw/2-iw/2)+1,1)
end
elseif background.resize == "center" then
lUtils.renderImg(rImg,math.floor(tw/2-w/2)+1,math.floor(th/2-h/2)+1,nil,true)
elseif background.resize == "repeat" or true then
local rw,rh = math.ceil(tw/w),math.ceil(th/h)
local frame = 1
for ry=0,rh-1 do
for rx=0,rw-1 do
if img then
lUtils.renderImg(img[frame],1+rx*w,1+ry*h,nil,true)
frame = frame+1
if frame > #img then
frame = 1
end
else
lUtils.renderImg(rImg,1+rx*w,1+ry*h,nil,true)
end
end
end
end
elseif background.color then
term.setBackgroundColor(background.color)
term.clear()
end
end
local selected = {}
local drag
local oldWidth,oldHeight = getDesktopSize()
local function render(dodebug)
term.redirect(terminal)
-- if not position gen position
-- make order table with IDs, then sort that table and iterate through
local dolog
if lOS.explorer.desktopBackground then
local dw,dh = lOS.explorer.desktopBackground.getSize()
local tw,th = term.getSize()
if dh ~= th-lOS.tbSize then
lOS.explorer.desktopBackground = nil
else
lOS.explorer.desktopBackground.setVisible(true)
lOS.explorer.desktopBackground.setVisible(false)
end
end
if not lOS.explorer.desktopBackground then
local col = dConfig.background.color or colors.white
local tw,th = term.getSize()
th = th-lOS.tbSize
local cTerm = term.current()
local bWin = window.create(term.current(),1,1,tw,th,true)
term.redirect(bWin)
term.setBackgroundColor(col)
term.clear()
if dConfig.background and dConfig.background[1] and type(dConfig.background[1]) == "table" then
for t=1,#dConfig.background do
drawBackground(dConfig.background[t])
end
else
drawBackground(dConfig.background)
end
term.redirect(cTerm)
lOS.explorer.desktopBackground = bWin
lOS.explorer.desktopBackground.setVisible(false)
end
term.setTextColor(dConfig.textColor or colors.black)
local newWidth,newHeight = getDesktopSize()
if oldWidth ~= newWidth or oldHeight ~= newHeight then
currentSize = newWidth..","..newHeight
oldWidth = newWidth
oldHeight = newHeight
if not dConfig.sizes[currentSize] then
dConfig.sizes[currentSize] = {}
end
clearDesktopMap()
end
local order = {}
for path,id in pairs(idReference) do
local absolutePath = fc("User/Desktop",path)
if not fs.exists(absolutePath) then
if dConfig.sizes[currentSize][id] then
local self = dConfig.sizes[currentSize][id]
if desktopMap[self.x] then
desktopMap[self.x][self.y] = nil
end
dConfig.sizes[currentSize][id] = nil
end
idReference[path] = nil
dConfig.files[id] = nil
else
table.insert(order,id)
end
end
table.sort(order)
local icons = dConfig.sizes[currentSize]
for i=1,#order do
local id = order[i]
if not icons[id] then
icons[id] = {}
end
if not icons[id].x or not icons[id].y then
local x,y = 1,1
local success = true
while desktopMap[x][y] do
y = y+1
if y > newHeight then
y = 1
x = x+1
if x > newWidth then
success = false
break
end
end
end
if success then
icons[id].x,icons[id].y = x,y
desktopMap[x][y] = id
end
end
if icons[id].x and icons[id].y and not selected[id] then
local x,y = icons[id].x,icons[id].y
desktopMap[x][y] = id
local path = fc("User/Desktop",dConfig.files[id])
local maxLines = 1
term.setBackgroundColor(colors.white)
lOS.explorer.drawIcon(path,1+(x-1)*8,1+(y-1)*5,false,true,maxLines,{"\24","0","8"})
end
--os.sleep(0.5)
end
for id,v in pairs(selected) do
if not icons[id] then
selected[id] = nil
else
local x,y = icons[id].x,icons[id].y
local path = fc("User/Desktop",dConfig.files[id])
local maxLines = 4
term.setBackgroundColor(colors.lightBlue)
lOS.explorer.drawIcon(path,1+(x-1)*8,1+(y-1)*5,false,false,maxLines,{"\24","0","8"})
end
end
if drag and drag.moved then
lOS.explorer.drawIcon(fc("User/Desktop",dConfig.files[drag.id]),drag.x-drag.ox,drag.y-drag.oy,false,true,1,{"\24","0","8"})
end
end
-- CONTEXT MENU FUNCTIONS
local fTypes = fileTypes
local function fExec(obj)
local f = obj.file
--[[local num = obj.num
local p = fTypes[f.type].c[2][num]]
local p = obj.p
if p == "Lua" then
lOS.execute(f.path)
else
lOS.execute(p.." "..f.path)
end
end
lOS.explorer.fExec = fExec
local function fOpenWin(obj)
local f = obj.file
lOS.execute("Program_Files/LevelOS/Explorer "..f.path)
end
lOS.explorer.fOpenWin = fOpenWin
function lOS.explorer.fRename(obj,s,sl,render,LevelOS,isDesktop)
if type(obj) == "string" then
if isDesktop then
for i,v in pairs(dConfig.sizes[currentSize]) do
_G.debugcompare = {}
if obj == fs.combine("User/Desktop",dConfig.files[i]) then
local f = genFile(obj)
local x,y = v.x,v.y
f.x1,f.y1 = 1+(x-1)*8,1+(y-1)*5
local lineH = math.min(#lUtils.wordwrap(lUtils.getFileName(f.path),7),4)
f.x2,f.y2 = f.x1+6,f.y1+3+lineH
obj = {file=f}
break
end
table.insert(debugcompare,{obj,fs.combine("User/Desktop",dConfig.files[i])})
end
if type(obj) == "string" then
error("not found")
end
else
for k,v in ipairs(sl.files) do
if v.path == obj then
obj = {file=v}
break
end
end
end
end
LevelOS.overlay = nil
local f = obj.file
sl.renaming = f.path
if isDesktop then
term.setBackgroundColor(colors.white)
term.setTextColor(colors.black)
else
term.setBackgroundColor(colors.pink)
opink = {s.win.getPaletteColor(colors.pink)}
sl.opink = opink
s.win.setPaletteColor(colors.pink,0.15,0.15,0.15)
term.setTextColor(colors.white)
end
local fname
local isShortcut = false
if lUtils.getFileType(f.path) == ".llnk" then
fname = lUtils.getFileName(f.path,false)
isShortcut = true
else
fname = lUtils.getFileName(f.path,true)
end
fname = fname:gsub(" ","_")
local w,h = term.getSize()
if sl.rMode == 1 then
sl.renamebox = lUtils.input(sl.NAME.x+2,f.y2,sl.NAME.x+sl.NAME.width-2,f.y2,{overflowX="scroll",overflowY="none",text=fname,selectColor=colors.blue})
sl.renamebox.x2 = math.min(math.max(sl.NAME.width,#sl.renamebox.txt+5),w)
else
sl.renamebox = lUtils.input(f.x1,f.y1+4,f.x2,f.y2,{overflowX="scroll",overflowY="none",text=fname,selectColor=colors.blue}) -- ah fuck ah FCK shit fuck
if isDesktop then
sl.renamebox.opt.selectTxtColor = colors.white
end
-- put renamebox in sl or something i guess FUCK why did i make it this way
end
local renamebox = sl.renamebox
renamebox.isShortcut = isShortcut
renamebox.scrollX = 0
renamebox.state = true
local ext = lUtils.getFileType(f.path)
if ext == ".llnk" then
ext = ""
end
renamebox.select = {1,#fname-#ext}
renamebox.cursor.a = 1+#fname-#ext
renamebox.cursor.x = renamebox.cursor.a
renamebox.update("timer")
local ok,err = pcall(function() render(true) end)
if not ok then
lOS.bsod(err)
end
renamebox.render()
os.queueEvent("renamebox")
end
local function fInvShort(obj,updateFiles,render)
local o = {lUtils.popup("Problem with shortcut","The item '"..obj.file.sName.."' that this shortcut refers to has been changed or moved, so this shortcut will no longer work properly.\n\nDo you want to delete this shortcut?",37,13,{"Yes","No"})}
if o[1] and o[3] == "Yes" then
fs.delete(obj.file.path)
updateFiles()
end
term.setTextColor(colors.white)
render()
end
lOS.explorer.fInvShort = fInvShort
local function genContextMenu(f,s,sl,selected,updateFiles,render,LevelOS,isDesktop)
local shortcache = lOS.explorer.shortcache
local winObj = term.current()
local function fInvShortInternal(obj)
term.redirect(winObj)
LevelOS.overlay = nil
lOS.noEvents = false
LevelOS.self.window.events = nil
fInvShort(obj,updateFiles,render)
term.setTextColor(colors.white)
end
local function fDelete(obj)
for k,v in pairs(selected) do
fs.delete(k)
end
selected = {}
updateFiles()
end
local function fOpen(obj)
local f = obj.file
selected = {}
sl.folder = f.path
updateFiles()
scrollY = 0
end
local function fCut(obj)
lOS.fClipboard = {}
for k,v in pairs(selected) do
table.insert(lOS.fClipboard,k)
end
lOS.fCut = lUtils.instantiate(lOS.fClipboard)
selected = {}
end
local function fCopy(obj)
lOS.fClipboard = {}
lOS.fCut = {}
for k,v in pairs(selected) do
table.insert(lOS.fClipboard,k)
end
end
local function fPaste(obj)
local tFolder = sl.folder
for c=1,#lOS.fClipboard do
if fs.exists(lOS.fClipboard[c]) == true then
fs.copy(lOS.fClipboard[c],uFP(fs.combine(tFolder,fs.getName(lOS.fClipboard[c]))))
end
if locateEntry(lOS.fCut,lOS.fClipboard[c]) == true then
fs.delete(lOS.fClipboard[c])
table.remove(lOS.fCut,({locateEntry(lOS.fCut,lOS.fClipboard[c])})[2])
end
end
lOS.fClipboard = {}
lOS.fCut = {}
updateFiles()
end
local opink
local function fRename(obj)
lOS.explorer.fRename(obj,s,sl,render,LevelOS,isDesktop)
if isDesktop then
local renamebox = sl.renamebox
term.redirect(terminal)
render()
renamebox.render()
lOS.noEvents = false
while true do
local e = {os.pullEventRaw()}
if e[1] == "char" and e[2] == " " then
e[2] = "_"
end
if e[1] == "key" and e[2] == keys.enter then
renamebox.state = false
--sl.renaming = nil
else
renamebox.update(unpack(e))
--render()
renamebox.render()
end
if not renamebox.state then
local pth = fs.combine("User/Desktop",renamebox.txt)
if renamebox.isShortcut then
pth = pth..".llnk"
end
if pth == sl.renaming or fs.combine(pth,"") == "User/Desktop" then
-- nothing
elseif fs.exists(pth) then
local newPth = uFP(pth)
local r = {lUtils.popup("Rename File","Do you want to rename \""..fs.getName(sl.renaming):gsub("_"," ").."\" to \""..fs.getName(newPth):gsub("_"," ").."\"?\n\nThere is already a file with the same name in this location.",41,11,{"Yes","No"})}
if r[1] and r[3] == "Yes" then
fs.move(sl.renaming,newPth)
end
else
fs.move(sl.renaming,pth)
end
renamebox = nil
sl.renamebox = nil
sl.renaming = nil
term.setCursorBlink(false)
updateFiles()
render()
break
end
end
end
end
local function fNew(obj)
local pth = uFP(fs.combine(sl.folder,"New_"..obj.txt:gsub(" ","_").."."..obj.format))
lUtils.fwrite(pth,obj.preset)
term.redirect(winObj)
updateFiles()
render()
fRename(pth)
-- rename
end
local function fNewFolder(obj)
local pth = uFP(fs.combine(sl.folder,"New_Folder"))
fs.makeDir(pth)
term.redirect(winObj)
updateFiles()
render()
fRename(pth)
-- rename
end
local function fOpenWith(obj)
if LevelOS and LevelOS.self and LevelOS.self.window then
LevelOS.self.window.events = nil
end
LevelOS.overlay = nil
lOS.noEvents = false
local p = sl.openWith(obj.file)
if p then
p = p.path
local f = obj.file
if p == "Lua" then
lOS.execute(f.path)
else
lOS.execute(p.." "..f.path)
end
end
end
local function fPin(obj)
table.insert(sl.config.quickaccess,obj.file.path)
sl.saveConfig()
sl.reloadQuickAccess()
end
local function fUnpin(obj)
local s,i = locateEntry(sl.config.quickaccess,obj.file.path)
if s then
table.remove(sl.config.quickaccess,i)
sl.saveConfig()
sl.reloadQuickAccess()
end
end
local function fShortcut(obj)
local pth = uFP(fs.combine(obj.folder,obj.name:gsub(" ","_").."_-_Shortcut.llnk"))
lUtils.asset.save({obj.file.path},pth)
updateFiles()
render()
end
local function fDesktopShortcut(obj)
lOS.genIco(obj.file.path)
--[[local pth = uFP(fs.combine(obj.folder,obj.name:gsub(" ","_")..".llnk"))
lUtils.asset.save({obj.file.path},pth)
os.queueEvent("explorer_reload")]]
end
local function fBackground(obj)
lOS.explorer.desktopConfig.background = {path=obj.file.path,resize=obj.txt:lower()}
os.queueEvent("explorer_reload")
end
local function fAddToBackground(obj)
local dConfig = lOS.explorer.desktopConfig
if not dConfig.background[1] then
local obg = dConfig.background
dConfig.background = {color=obg.color,obg}
end
table.insert(dConfig.background,{path=obj.file.path,resize=obj.txt:lower()})
os.queueEvent("explorer_reload")
lOS.explorer.desktopBackground = nil
end
local function fCopyPath(obj)
ccemux.setClipboard(obj.file.path)
end
if isDesktop then
fOpen = fOpenWin
end
local options
if not f then
local newfiles = {}
for k,v in pairs(fTypes) do
if type(v) == "table" and type(v.emptyFilePreset) == "string" then
table.insert(newfiles,{txt=v.name,preset=v.emptyFilePreset,action=fNew,format=k})
end
end
table.insert(newfiles,{txt="bImg Icon",preset='{{{" "," "," "},{" "," "," "},{" "," "," "}}}',action=fNew,format="bimg"})
local opt = {
{{txt="Refresh",action=updateFiles}},
{{txt="Paste",action=fPaste,disabled=(#lOS.fClipboard == 0 or fs.isReadOnly(sl.folder))}},
{{txt="New",disabled=(fs.isReadOnly(sl.folder)),action={{txt="Folder",action=fNewFolder},newfiles}}} -- import from formats.lconf
}
if isDesktop then
local txtcolors = {}
for k,v in pairs(colors) do
if type(v) == "number" then
table.insert(txtcolors,k)
end
end
table.sort(txtcolors)
local txtcoloroptions = {}
for c=1,#txtcolors do
local col = colors[txtcolors[c]]
local name = txtcolors[c]:sub(1,1):upper()..txtcolors[c]:sub(2)
local b = lUtils.toBlit(col)
if dConfig.textColor == col then
table.insert(txtcoloroptions,{txt={" \7 "..name,nil,string.rep(b,3)}})
if col == colors.white then
txtcoloroptions[#txtcoloroptions].txt[2] = "fff"
end
else
table.insert(txtcoloroptions,{txt={" "..name,nil,string.rep(b,3)},action=function() dConfig.textColor = col updateFiles() render() end})
end
end
if not dConfig.textColor then
table.insert(txtcoloroptions,1,{txt=" \7 Automatic"})
else
table.insert(txtcoloroptions,1,{txt=" Automatic",action=function() dConfig.textColor = nil updateFiles() render() end})
end
table.insert(opt[1],1,{txt="View",action={
{txt="Text color",action={txtcoloroptions}}
}})
end
if ccemux then
table.insert(opt,{{txt="Open External Explorer",action=function() ccemux.openDataDir() end}})
end
options = opt
else
if type(f) == "string" then
f = genFile(f)
end
local file = f.path
local nf = f
local invalidShort
if shortcache[file] then
f.sPath = shortcache[file][1]
f.sName = fs.getName(f.sPath)
if fs.exists(f.sPath) then
nf = genFile(f.sPath)
else
invalidShort = true
end
end
options = {
{
},
{
{txt="Cut",action=fCut},
{txt="Copy",action=fCopy}
},
{
{txt="Delete",action=fDelete},
{txt="Rename",action=fRename,file=f,sl=sl}
}
}
local createshortcut = {txt="Create shortcut",file=nf,folder=sl.folder,name=nf.name,action=fShortcut}
local createshortcut2 = {txt="Create shortcut on desktop",file=nf,folder="User/Desktop",name=nf.name,action=fDesktopShortcut}
if fs.getName(nf.path) == "main.lua" then
createshortcut.name = fs.getName(fs.getDir(nf.path))
createshortcut2.name = fs.getName(fs.getDir(nf.path))
end
if invalidShort then
createshortcut.action = fInvShortInternal
createshortcut2.action = fInvShortInternal
end
if not isDesktop then
table.insert(options[3],1,createshortcut2)
table.insert(options[3],1,createshortcut)
end
local disabled = {}
if f.readOnly == true then
disabled = {Edit=true,Cut=true,Delete=true,Rename=true}
elseif disks[file] ~= nil then
disabled = {Edit=true,Cut=true,Delete=true,Rename=true}
disabled["Create Shortcut"] = true
end
local fc = fs.combine
if ccemux then
table.insert(options[3],{txt="Copy path",file=f,action=fCopyPath})
end
local fOpen = fOpen
local fOpenWin = fOpenWin
local fOpenWith = fOpenWith
local fExec = fExec
local fUnpin = fUnpin
local fPin = fPin
if invalidShort then
fOpen = fInvShortInternal
fOpenWin = fInvShortInternal
fOpenWith = fInvShortInternal
fExec = fInvShortInternal
fUnpin = fInvShortInternal
fPin = fInvShortInternal
end
if nf.isDir then
local etxt = ""
local isProgram = false
if fs.exists(fc(nf.path,"main.lua")) then
isProgram = true
etxt = "folder "
end
options[1] = {{txt="Open "..etxt,action=fOpen,file=nf}}
if not isDesktop then
table.insert(options[1],{txt="Open "..etxt.."in new window",action=fOpenWin,file=nf})
end
if isProgram then
table.insert(options[1],{txt="Execute",action=fExec,p="Lua",file=nf})
createshortcut.action = {{txt="To program",file=genFile(fc(nf.path,"main.lua")),folder=sl.folder,name=nf.name,action=fShortcut},{txt="To folder",file=nf,folder=sl.folder,name=nf.name,action=fShortcut}}
end
if not isDesktop then
if locateEntry(sl.config.quickaccess,nf.path) then
table.insert(options[1],{txt="Unpin from Quick access",action=fUnpin,file=nf})
else
table.insert(options[1],{txt="Pin to Quick access",action=fPin,file=nf})
end
end
elseif fTypes[nf.type] and fTypes[nf.type].contextMenu and not invalidShort then
local tbl = {}
for k,v in ipairs(fTypes[nf.type].contextMenu[1]) do
table.insert(tbl,{txt=v,file=nf,p=fTypes[nf.type].contextMenu[2][k],action=fExec})
end
if nf.type == "bimg" or nf.type == "limg" or nf.type == "nfp" then
table.insert(tbl,{txt="Set as desktop background",action={{{txt="Stretch",file=nf,action=fBackground},{txt="Repeat",file=nf,action=fBackground},{txt="Center",file=nf,action=fBackground}}}})
table.insert(tbl,{txt="Add to desktop background",action={{{txt="Repeat",file=nf,action=fAddToBackground},{txt="Center",file=nf,action=fAddToBackground}}},disabled=(not lOS.explorer.desktopConfig.background)})
end
options[1] = tbl
else
options[1] = {{txt="Edit",action=fExec,p="rom/programs/edit.lua",file=nf}}
end
if shortcache[file] then
if invalidShort then
table.insert(options[1],2,{txt="Open file location",disabled=true})
else
table.insert(options[1],2,{txt="Open file location",action=fOpen,file=genFile(fs.getDir(nf.path))})
end
end
local ow
if fTypes[nf.type] and fTypes[nf.type].openWith and not nf.isDir then
local owtbl = {}
for k,v in ipairs(fTypes[nf.type].openWith) do
table.insert(owtbl,{txt=lUtils.getFileName(v),file=nf,p=v,action=fExec})
end
ow = {txt="Open with",action={owtbl,{txt="Choose another app",action=fOpenWith,file=nf,disabled=isDesktop}}}
if invalidShort then
ow.disabled = true
end
table.insert(options[1],ow)
end
if nf.readOnly == true --[[and locateEntry(options[1],"Edit") == true]] then
for k,v in ipairs(options[1]) do
if type(v) == "table" and v.txt == "Edit" then
v.txt = "View"
end
end
--options[1][({locateEntry(options[1],"Edit")})[2]] = "View"
end
for _,cat in ipairs(options) do
for k,v in ipairs(cat) do
if type(v) == "string" and disabled[v] then
cat[k] = {txt=v,disabled=true}
elseif type(v) == "table" and disabled[v.txt] then
v.disabled = true
end
end
end
end
return options
end
lOS.explorer.genContextMenu = genContextMenu
local function cMenu(f, x, y)
local sel = {}
for k,v in pairs(selected) do
local path = fc("User/Desktop",dConfig.files[k])
sel[path] = true
end
local options = genContextMenu(f,{win=term.current()},{folder="User/Desktop"},sel,reloadDesktop,render,LevelOS,true)
local output = {lOS.contextmenu(x,y,30,options,nil,true)}
return unpack(output)
end
render()
os.sleep(0.05)
render()
local lastUpdate = os.epoch("utc")
local tID = os.startTimer(10)
while true do
local e = {os.pullEvent()}
if e[1] == "explorer_full_reload" then
lOS.explorer.shortcache = {}
lOS.explorer.imgcache = {}
lOS.explorer.nimgcache = {}
lOS.explorer.desktopBackground = nil
reloadDesktop()
render()
elseif e[1] == "term_resize" or e[1] == "explorer_reload" then
lOS.explorer.desktopBackground = nil
reloadDesktop()
render()
elseif e[1]:find("mouse") and e[3] and e[4] then
local x,y = math.ceil(e[3]/8),math.ceil(e[4]/5)
if e[1] == "mouse_click" then
lOS.focusWin = lOS.wins[0]
end
if e[1] == "mouse_drag" and drag then
drag.x,drag.y = e[3],e[4]
drag.moved = true
render()
elseif e[1] == "mouse_up" and drag then
if not desktopMap[x][y] then
desktopMap[x][y] = drag.id
local icon = dConfig.sizes[currentSize][drag.id]
desktopMap[icon.x][icon.y] = nil
icon.x,icon.y = x,y
lUtils.asset.save(dConfig,"LevelOS/data/desktop.lconf")
end
drag = nil
render()
elseif e[3]%8 ~= 0 and desktopMap[x][y] then
if e[1] == "mouse_click" then
local id = desktopMap[x][y]
if selected[id] and os.epoch("utc") < selected[id]+500 and e[2] == 1 then
local path = fc("User/Desktop",dConfig.files[id])
local rPath = path
local cmd = path
local shortcache = lOS.explorer.shortcache
local exec = false
local invalidShort = false
local f = genFile(path)
if shortcache[path] then
cmd = shortcache[path][1]
rPath = cmd
f.sName = rPath
if not fs.exists(rPath) then
invalidShort = true
end
if shortcache[path].args and #shortcache[path].args > 0 then
cmd = cmd.." "..table.concat(shortcache[path].args)
end
if shortcache[path].program then
if shortcache[path].program ~= "Lua" then
cmd = shortcache[path].program.." "..cmd
end
exec = true
end
end
if invalidShort then
fInvShort({file=f},reloadDesktop,render)
else
local fType = lUtils.getFileType(rPath):sub(2)
if exec then
lOS.execute(cmd)
elseif fs.isDir(rPath) then
lOS.execute("Program_Files/LevelOS/Explorer "..cmd)
elseif fileTypes[fType] and fileTypes[fType].program then
local p = fileTypes[fType].program
if p == "Lua" then
lOS.execute(cmd)
else
lOS.execute(p.." "..cmd)
end
elseif fType == "" then
lOS.execute(cmd)
end
end
selected = {}
else
if not (lUtils.isHolding(keys.leftCtrl) or lUtils.isHolding(keys.leftShift)) then
selected = {}
end
if e[2] == 1 then
selected[id] = os.epoch("utc")
drag = {id=id,ox=(e[3]-1)%8,oy=(e[4]-1)%5,x=e[3],y=e[4],moved=false}
else
selected[id] = 0
end
end
render()
elseif e[1] == "mouse_up" and e[2] == 2 then
local id = desktopMap[x][y]
local path = fc("User/Desktop",dConfig.files[id])
--[[local rPath = path
local cmd = path
local shortcache = lOS.explorer.shortcache
local exec = false]]
local f = genFile(path)
f.x1,f.y1 = 1+(x-1)*8,1+(y-1)*5
local lineH = math.min(#lUtils.wordwrap(lUtils.getFileName(f.path),7),4)
f.x2,f.y2 = f.x1+6,f.y1+3+lineH
cMenu(f,e[3],e[4])
end
elseif e[1] == "mouse_click" and not (lUtils.isHolding(keys.leftCtrl) or lUtils.isHolding(keys.leftShift)) then
selected = {}
render()
if e[2] == 2 then
cMenu(nil, e[3], e[4])
end
end
elseif (e[1] == "timer" and e[2] == tID) or os.epoch("utc")-10000 > lastUpdate then
lastUpdate = os.epoch("utc")
if not (e[1] == "timer" and e[2] == tID) then
os.cancelTimer(tID)
end
tID = os.startTimer(10)
reloadDesktop()
render()
end
if e[1] == "mouse_up" then
drag = nil
end
end