1
0
mirror of https://github.com/kepler155c/opus synced 2025-11-26 12:04:52 +00:00

spaces->tabs + cleanup + pathing fixes

This commit is contained in:
kepler155c@gmail.com
2019-06-18 15:19:24 -04:00
parent 82ec4db50f
commit 3c22a872b0
37 changed files with 1948 additions and 1703 deletions

View File

@@ -15,11 +15,11 @@ local FILE = 1
UI:configure('Files', ...)
local config = Config.load('Files', {
showHidden = false,
showDirSizes = false,
showHidden = false,
showDirSizes = false,
})
config.associations = config.associations or {
nft = 'pain',
nft = 'pain',
}
local copied = { }
@@ -28,517 +28,517 @@ local directories = { }
local cutMode = false
local function formatSize(size)
if size >= 1000000 then
return string.format('%dM', math.floor(size/1000000, 2))
elseif size >= 1000 then
return string.format('%dK', math.floor(size/1000, 2))
end
return size
if size >= 1000000 then
return string.format('%dM', math.floor(size/1000000, 2))
elseif size >= 1000 then
return string.format('%dK', math.floor(size/1000, 2))
end
return size
end
local Browser = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = '^-', event = 'updir' },
{ text = 'File', dropdown = {
{ text = 'Run', event = 'run', flags = FILE },
{ text = 'Edit e', event = 'edit', flags = FILE },
{ text = 'Cloud edit c', event = 'cedit', flags = FILE },
{ text = 'Pastebin put p', event = 'pastebin', flags = FILE },
{ text = 'Shell s', event = 'shell' },
{ spacer = true },
{ text = 'Quit q', event = 'quit' },
} },
{ text = 'Edit', dropdown = {
{ text = 'Cut ^x', event = 'cut' },
{ text = 'Copy ^c', event = 'copy' },
{ text = 'Copy path ', event = 'copy_path' },
{ text = 'Paste ^v', event = 'paste' },
{ spacer = true },
{ text = 'Mark m', event = 'mark' },
{ text = 'Unmark all u', event = 'unmark' },
{ spacer = true },
{ text = 'Delete del', event = 'delete' },
} },
{ text = 'View', dropdown = {
{ text = 'Refresh r', event = 'refresh' },
{ text = 'Hidden ^h', event = 'toggle_hidden' },
{ text = 'Dir Size ^s', event = 'toggle_dirSize' },
} },
{ text = '\187',
x = -3,
dropdown = {
{ text = 'Associations', event = 'associate' },
} },
},
},
grid = UI.ScrollingGrid {
columns = {
{ heading = 'Name', key = 'name' },
{ key = 'flags', width = 2 },
{ heading = 'Size', key = 'fsize', width = 5 },
},
sortColumn = 'name',
y = 2, ey = -2,
},
statusBar = UI.StatusBar {
columns = {
{ key = 'status' },
{ key = 'totalSize', width = 6 },
},
},
notification = UI.Notification { },
associations = UI.SlideOut {
backgroundColor = colors.cyan,
menuBar = UI.MenuBar {
buttons = {
{ text = 'Save', event = 'save' },
{ text = 'Cancel', event = 'cancel' },
},
},
grid = UI.ScrollingGrid {
x = 2, ex = -6, y = 3, ey = -5,
columns = {
{ heading = 'Extension', key = 'name' },
{ heading = 'Program', key = 'value' },
},
autospace = true,
sortColumn = 'name',
accelerators = {
delete = 'remove_entry',
},
},
remove = UI.Button {
x = -4, y = 6,
text = '-', event = 'remove_entry', help = 'Remove',
},
form = UI.Form {
x = 3, y = -3, ey = -2,
margin = 1,
manualControls = true,
[1] = UI.TextEntry {
width = 20,
formLabel = 'Extension', formKey = 'name',
shadowText = 'extension',
required = true,
limit = 64,
},
[2] = UI.TextEntry {
width = 20,
formLabel = 'Program', formKey = 'value',
shadowText = 'program',
required = true,
limit = 128,
},
add = UI.Button {
x = -11, y = 1,
text = 'Add', event = 'add_association',
},
},
statusBar = UI.StatusBar {
backgroundColor = colors.cyan,
},
},
accelerators = {
q = 'quit',
c = 'cedit',
e = 'edit',
s = 'shell',
p = 'pastebin',
r = 'refresh',
[ ' ' ] = 'mark',
m = 'mark',
backspace = 'updir',
u = 'unmark',
d = 'delete',
delete = 'delete',
[ 'control-h' ] = 'toggle_hidden',
[ 'control-s' ] = 'toggle_dirSize',
[ 'control-x' ] = 'cut',
[ 'control-c' ] = 'copy',
paste = 'paste',
},
menuBar = UI.MenuBar {
buttons = {
{ text = '^-', event = 'updir' },
{ text = 'File', dropdown = {
{ text = 'Run', event = 'run', flags = FILE },
{ text = 'Edit e', event = 'edit', flags = FILE },
{ text = 'Cloud edit c', event = 'cedit', flags = FILE },
{ text = 'Pastebin put p', event = 'pastebin', flags = FILE },
{ text = 'Shell s', event = 'shell' },
{ spacer = true },
{ text = 'Quit q', event = 'quit' },
} },
{ text = 'Edit', dropdown = {
{ text = 'Cut ^x', event = 'cut' },
{ text = 'Copy ^c', event = 'copy' },
{ text = 'Copy path ', event = 'copy_path' },
{ text = 'Paste ^v', event = 'paste' },
{ spacer = true },
{ text = 'Mark m', event = 'mark' },
{ text = 'Unmark all u', event = 'unmark' },
{ spacer = true },
{ text = 'Delete del', event = 'delete' },
} },
{ text = 'View', dropdown = {
{ text = 'Refresh r', event = 'refresh' },
{ text = 'Hidden ^h', event = 'toggle_hidden' },
{ text = 'Dir Size ^s', event = 'toggle_dirSize' },
} },
{ text = '\187',
x = -3,
dropdown = {
{ text = 'Associations', event = 'associate' },
} },
},
},
grid = UI.ScrollingGrid {
columns = {
{ heading = 'Name', key = 'name' },
{ key = 'flags', width = 2 },
{ heading = 'Size', key = 'fsize', width = 5 },
},
sortColumn = 'name',
y = 2, ey = -2,
},
statusBar = UI.StatusBar {
columns = {
{ key = 'status' },
{ key = 'totalSize', width = 6 },
},
},
notification = UI.Notification { },
associations = UI.SlideOut {
backgroundColor = colors.cyan,
menuBar = UI.MenuBar {
buttons = {
{ text = 'Save', event = 'save' },
{ text = 'Cancel', event = 'cancel' },
},
},
grid = UI.ScrollingGrid {
x = 2, ex = -6, y = 3, ey = -5,
columns = {
{ heading = 'Extension', key = 'name' },
{ heading = 'Program', key = 'value' },
},
autospace = true,
sortColumn = 'name',
accelerators = {
delete = 'remove_entry',
},
},
remove = UI.Button {
x = -4, y = 6,
text = '-', event = 'remove_entry', help = 'Remove',
},
form = UI.Form {
x = 3, y = -3, ey = -2,
margin = 1,
manualControls = true,
[1] = UI.TextEntry {
width = 20,
formLabel = 'Extension', formKey = 'name',
shadowText = 'extension',
required = true,
limit = 64,
},
[2] = UI.TextEntry {
width = 20,
formLabel = 'Program', formKey = 'value',
shadowText = 'program',
required = true,
limit = 128,
},
add = UI.Button {
x = -11, y = 1,
text = 'Add', event = 'add_association',
},
},
statusBar = UI.StatusBar {
backgroundColor = colors.cyan,
},
},
accelerators = {
q = 'quit',
c = 'cedit',
e = 'edit',
s = 'shell',
p = 'pastebin',
r = 'refresh',
[ ' ' ] = 'mark',
m = 'mark',
backspace = 'updir',
u = 'unmark',
d = 'delete',
delete = 'delete',
[ 'control-h' ] = 'toggle_hidden',
[ 'control-s' ] = 'toggle_dirSize',
[ 'control-x' ] = 'cut',
[ 'control-c' ] = 'copy',
paste = 'paste',
},
}
function Browser:enable()
UI.Page.enable(self)
self:setFocus(self.grid)
UI.Page.enable(self)
self:setFocus(self.grid)
end
function Browser.menuBar:getActive(menuItem)
local file = Browser.grid:getSelected()
if menuItem.flags == FILE then
return file and not file.isDir
end
return true
local file = Browser.grid:getSelected()
if menuItem.flags == FILE then
return file and not file.isDir
end
return true
end
function Browser.grid:sortCompare(a, b)
if self.sortColumn == 'fsize' then
return a.size < b.size
elseif self.sortColumn == 'flags' then
return a.flags < b.flags
end
if a.isDir == b.isDir then
return a.name:lower() < b.name:lower()
end
return a.isDir
if self.sortColumn == 'fsize' then
return a.size < b.size
elseif self.sortColumn == 'flags' then
return a.flags < b.flags
end
if a.isDir == b.isDir then
return a.name:lower() < b.name:lower()
end
return a.isDir
end
function Browser.grid:getRowTextColor(file)
if file.marked then
return colors.green
end
if file.isDir then
return colors.cyan
end
if file.isReadOnly then
return colors.pink
end
return colors.white
if file.marked then
return colors.green
end
if file.isDir then
return colors.cyan
end
if file.isReadOnly then
return colors.pink
end
return colors.white
end
function Browser.grid:eventHandler(event)
if event.type == 'copy' then -- let copy be handled by parent
return false
end
return UI.ScrollingGrid.eventHandler(self, event)
if event.type == 'copy' then -- let copy be handled by parent
return false
end
return UI.ScrollingGrid.eventHandler(self, event)
end
function Browser.statusBar:draw()
if self.parent.dir then
local info = '#:' .. Util.size(self.parent.dir.files)
local numMarked = Util.size(marked)
if numMarked > 0 then
info = info .. ' M:' .. numMarked
end
self:setValue('info', info)
self:setValue('totalSize', formatSize(self.parent.dir.totalSize))
UI.StatusBar.draw(self)
end
if self.parent.dir then
local info = '#:' .. Util.size(self.parent.dir.files)
local numMarked = Util.size(marked)
if numMarked > 0 then
info = info .. ' M:' .. numMarked
end
self:setValue('info', info)
self:setValue('totalSize', formatSize(self.parent.dir.totalSize))
UI.StatusBar.draw(self)
end
end
function Browser:setStatus(status, ...)
self.notification:info(string.format(status, ...))
self.notification:info(string.format(status, ...))
end
function Browser:unmarkAll()
for _,m in pairs(marked) do
m.marked = false
end
Util.clear(marked)
for _,m in pairs(marked) do
m.marked = false
end
Util.clear(marked)
end
function Browser:getDirectory(directory)
local s, dir = pcall(function()
local s, dir = pcall(function()
local dir = directories[directory]
if not dir then
dir = {
name = directory,
size = 0,
files = { },
totalSize = 0,
index = 1
}
directories[directory] = dir
end
local dir = directories[directory]
if not dir then
dir = {
name = directory,
size = 0,
files = { },
totalSize = 0,
index = 1
}
directories[directory] = dir
end
self:updateDirectory(dir)
self:updateDirectory(dir)
return dir
end)
return dir
end)
return s, dir
return s, dir
end
function Browser:updateDirectory(dir)
dir.size = 0
dir.totalSize = 0
Util.clear(dir.files)
dir.size = 0
dir.totalSize = 0
Util.clear(dir.files)
local files = fs.listEx(dir.name)
if files then
dir.size = #files
for _, file in pairs(files) do
file.fullName = fs.combine(dir.name, file.name)
file.flags = ''
if not file.isDir then
dir.totalSize = dir.totalSize + file.size
file.fsize = formatSize(file.size)
else
if config.showDirSizes then
file.size = fs.getSize(file.fullName, true)
local files = fs.listEx(dir.name)
if files then
dir.size = #files
for _, file in pairs(files) do
file.fullName = fs.combine(dir.name, file.name)
file.flags = ''
if not file.isDir then
dir.totalSize = dir.totalSize + file.size
file.fsize = formatSize(file.size)
else
if config.showDirSizes then
file.size = fs.getSize(file.fullName, true)
dir.totalSize = dir.totalSize + file.size
file.fsize = formatSize(file.size)
end
file.flags = 'D'
end
if file.isReadOnly then
file.flags = file.flags .. 'R'
end
if config.showHidden or file.name:sub(1, 1) ~= '.' then
dir.files[file.fullName] = file
end
end
end
dir.totalSize = dir.totalSize + file.size
file.fsize = formatSize(file.size)
end
file.flags = 'D'
end
if file.isReadOnly then
file.flags = file.flags .. 'R'
end
if config.showHidden or file.name:sub(1, 1) ~= '.' then
dir.files[file.fullName] = file
end
end
end
-- self.grid:update()
-- self.grid:setIndex(dir.index)
self.grid:setValues(dir.files)
self.grid:setValues(dir.files)
end
function Browser:setDir(dirName, noStatus)
self:unmarkAll()
self:unmarkAll()
if self.dir then
self.dir.index = self.grid:getIndex()
end
local DIR = fs.combine('', dirName)
shell.setDir(DIR)
local s, dir = self:getDirectory(DIR)
if s then
self.dir = dir
elseif noStatus then
error(dir)
else
self:setStatus(dir)
self:setDir('', true)
return
end
if self.dir then
self.dir.index = self.grid:getIndex()
end
local DIR = fs.combine('', dirName)
shell.setDir(DIR)
local s, dir = self:getDirectory(DIR)
if s then
self.dir = dir
elseif noStatus then
error(dir)
else
self:setStatus(dir)
self:setDir('', true)
return
end
if not noStatus then
self.statusBar:setValue('status', '/' .. self.dir.name)
self.statusBar:draw()
end
self.grid:setIndex(self.dir.index)
if not noStatus then
self.statusBar:setValue('status', '/' .. self.dir.name)
self.statusBar:draw()
end
self.grid:setIndex(self.dir.index)
end
function Browser:run(...)
if multishell then
local tabId = shell.openTab(...)
multishell.setFocus(tabId)
else
shell.run(...)
Event.terminate = false
self:draw()
end
if multishell then
local tabId = shell.openTab(...)
multishell.setFocus(tabId)
else
shell.run(...)
Event.terminate = false
self:draw()
end
end
function Browser:hasMarked()
if Util.size(marked) == 0 then
local file = self.grid:getSelected()
if file then
file.marked = true
marked[file.fullName] = file
self.grid:draw()
end
end
return Util.size(marked) > 0
if Util.size(marked) == 0 then
local file = self.grid:getSelected()
if file then
file.marked = true
marked[file.fullName] = file
self.grid:draw()
end
end
return Util.size(marked) > 0
end
function Browser:eventHandler(event)
local file = self.grid:getSelected()
local file = self.grid:getSelected()
if event.type == 'quit' then
Event.exitPullEvents()
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'edit' and file then
self:run('edit', file.name)
elseif event.type == 'edit' and file then
self:run('edit', file.name)
elseif event.type == 'cedit' and file then
self:run('cedit', file.name)
self:setStatus('Started cloud edit')
elseif event.type == 'cedit' and file then
self:run('cedit', file.name)
self:setStatus('Started cloud edit')
elseif event.type == 'shell' then
self:run('sys/apps/shell.lua')
elseif event.type == 'shell' then
self:run('sys/apps/shell.lua')
elseif event.type == 'refresh' then
self:updateDirectory(self.dir)
self.grid:draw()
self:setStatus('Refreshed')
elseif event.type == 'refresh' then
self:updateDirectory(self.dir)
self.grid:draw()
self:setStatus('Refreshed')
elseif event.type == 'associate' then
self.associations:show()
elseif event.type == 'associate' then
self.associations:show()
elseif event.type == 'pastebin' then
if file and not file.isDir then
local s, m = pastebin.put(file.fullName)
if s then
os.queueEvent('clipboard_copy', s)
self.notification:success(string.format('Uploaded as %s', s), 0)
else
self.notification:error(m)
end
end
elseif event.type == 'pastebin' then
if file and not file.isDir then
local s, m = pastebin.put(file.fullName)
if s then
os.queueEvent('clipboard_copy', s)
self.notification:success(string.format('Uploaded as %s', s), 0)
else
self.notification:error(m)
end
end
elseif event.type == 'toggle_hidden' then
config.showHidden = not config.showHidden
Config.update('Files', config)
elseif event.type == 'toggle_hidden' then
config.showHidden = not config.showHidden
Config.update('Files', config)
self:updateDirectory(self.dir)
self.grid:draw()
if not config.showHidden then
self:setStatus('Hiding hidden')
else
self:setStatus('Displaying hidden')
end
self:updateDirectory(self.dir)
self.grid:draw()
if not config.showHidden then
self:setStatus('Hiding hidden')
else
self:setStatus('Displaying hidden')
end
elseif event.type == 'toggle_dirSize' then
config.showDirSizes = not config.showDirSizes
Config.update('Files', config)
elseif event.type == 'toggle_dirSize' then
config.showDirSizes = not config.showDirSizes
Config.update('Files', config)
self:updateDirectory(self.dir)
self.grid:draw()
if config.showDirSizes then
self:setStatus('Displaying dir sizes')
end
self:updateDirectory(self.dir)
self.grid:draw()
if config.showDirSizes then
self:setStatus('Displaying dir sizes')
end
elseif event.type == 'mark' and file then
file.marked = not file.marked
if file.marked then
marked[file.fullName] = file
else
marked[file.fullName] = nil
end
self.grid:draw()
self.statusBar:draw()
elseif event.type == 'mark' and file then
file.marked = not file.marked
if file.marked then
marked[file.fullName] = file
else
marked[file.fullName] = nil
end
self.grid:draw()
self.statusBar:draw()
elseif event.type == 'unmark' then
self:unmarkAll()
self.grid:draw()
self:setStatus('Marked files cleared')
elseif event.type == 'unmark' then
self:unmarkAll()
self.grid:draw()
self:setStatus('Marked files cleared')
elseif event.type == 'grid_select' or event.type == 'run' then
if file then
if file.isDir then
self:setDir(file.fullName)
else
local ext = file.name:match('%.(%w+)$')
if ext and config.associations[ext] then
self:run(config.associations[ext], '/' .. file.fullName)
else
self:run(file.name)
end
end
end
elseif event.type == 'grid_select' or event.type == 'run' then
if file then
if file.isDir then
self:setDir(file.fullName)
else
local ext = file.name:match('%.(%w+)$')
if ext and config.associations[ext] then
self:run(config.associations[ext], '/' .. file.fullName)
else
self:run(file.name)
end
end
end
elseif event.type == 'updir' then
local dir = (self.dir.name:match("(.*/)"))
self:setDir(dir or '/')
elseif event.type == 'updir' then
local dir = (self.dir.name:match("(.*/)"))
self:setDir(dir or '/')
elseif event.type == 'delete' then
if self:hasMarked() then
local width = self.statusBar:getColumnWidth('status')
self.statusBar:setColumnWidth('status', UI.term.width)
self.statusBar:setValue('status', 'Delete marked? (y/n)')
self.statusBar:draw()
self.statusBar:sync()
local _, ch = os.pullEvent('char')
if ch == 'y' or ch == 'Y' then
for _,m in pairs(marked) do
pcall(function()
fs.delete(m.fullName)
end)
end
end
marked = { }
self.statusBar:setColumnWidth('status', width)
self.statusBar:setValue('status', '/' .. self.dir.name)
self:updateDirectory(self.dir)
elseif event.type == 'delete' then
if self:hasMarked() then
local width = self.statusBar:getColumnWidth('status')
self.statusBar:setColumnWidth('status', UI.term.width)
self.statusBar:setValue('status', 'Delete marked? (y/n)')
self.statusBar:draw()
self.statusBar:sync()
local _, ch = os.pullEvent('char')
if ch == 'y' or ch == 'Y' then
for _,m in pairs(marked) do
pcall(function()
fs.delete(m.fullName)
end)
end
end
marked = { }
self.statusBar:setColumnWidth('status', width)
self.statusBar:setValue('status', '/' .. self.dir.name)
self:updateDirectory(self.dir)
self.statusBar:draw()
self.grid:draw()
self:setFocus(self.grid)
end
self.statusBar:draw()
self.grid:draw()
self:setFocus(self.grid)
end
elseif event.type == 'copy' or event.type == 'cut' then
if self:hasMarked() then
cutMode = event.type == 'cut'
Util.clear(copied)
Util.merge(copied, marked)
--self:unmarkAll()
self.grid:draw()
self:setStatus('Copied %d file(s)', Util.size(copied))
end
elseif event.type == 'copy' or event.type == 'cut' then
if self:hasMarked() then
cutMode = event.type == 'cut'
Util.clear(copied)
Util.merge(copied, marked)
--self:unmarkAll()
self.grid:draw()
self:setStatus('Copied %d file(s)', Util.size(copied))
end
elseif event.type == 'copy_path' then
if file then
os.queueEvent('clipboard_copy', file.fullName)
end
elseif event.type == 'copy_path' then
if file then
os.queueEvent('clipboard_copy', file.fullName)
end
elseif event.type == 'paste' then
for _,m in pairs(copied) do
local s, m = pcall(function()
if cutMode then
fs.move(m.fullName, fs.combine(self.dir.name, m.name))
else
fs.copy(m.fullName, fs.combine(self.dir.name, m.name))
end
end)
end
self:updateDirectory(self.dir)
self.grid:draw()
self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)')
elseif event.type == 'paste' then
for _,m in pairs(copied) do
local s, m = pcall(function()
if cutMode then
fs.move(m.fullName, fs.combine(self.dir.name, m.name))
else
fs.copy(m.fullName, fs.combine(self.dir.name, m.name))
end
end)
end
self:updateDirectory(self.dir)
self.grid:draw()
self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)')
else
return UI.Page.eventHandler(self, event)
end
self:setFocus(self.grid)
return true
else
return UI.Page.eventHandler(self, event)
end
self:setFocus(self.grid)
return true
end
--[[ Associations slide out ]] --
function Browser.associations:show()
self.grid.values = { }
for k, v in pairs(config.associations) do
table.insert(self.grid.values, {
name = k,
value = v,
})
end
self.grid:update()
UI.SlideOut.show(self)
self:setFocus(self.form[1])
self.grid.values = { }
for k, v in pairs(config.associations) do
table.insert(self.grid.values, {
name = k,
value = v,
})
end
self.grid:update()
UI.SlideOut.show(self)
self:setFocus(self.form[1])
end
function Browser.associations:eventHandler(event)
if event.type == 'remove_entry' then
local row = self.grid:getSelected()
if row then
Util.removeByValue(self.grid.values, row)
self.grid:update()
self.grid:draw()
end
if event.type == 'remove_entry' then
local row = self.grid:getSelected()
if row then
Util.removeByValue(self.grid.values, row)
self.grid:update()
self.grid:draw()
end
elseif event.type == 'add_association' then
if self.form:save() then
local entry = Util.find(self.grid.values, 'name', self.form[1].value) or { }
entry.name = self.form[1].value
entry.value = self.form[2].value
table.insert(self.grid.values, entry)
self.form[1]:reset()
self.form[2]:reset()
self.grid:update()
self.grid:draw()
end
elseif event.type == 'add_association' then
if self.form:save() then
local entry = Util.find(self.grid.values, 'name', self.form[1].value) or { }
entry.name = self.form[1].value
entry.value = self.form[2].value
table.insert(self.grid.values, entry)
self.form[1]:reset()
self.form[2]:reset()
self.grid:update()
self.grid:draw()
end
elseif event.type == 'cancel' then
self:hide()
elseif event.type == 'cancel' then
self:hide()
elseif event.type == 'save' then
config.associations = { }
for _, v in pairs(self.grid.values) do
config.associations[v.name] = v.value
end
Config.update('Files', config)
self:hide()
elseif event.type == 'save' then
config.associations = { }
for _, v in pairs(self.grid.values) do
config.associations[v.name] = v.value
end
Config.update('Files', config)
self:hide()
else
return UI.SlideOut.eventHandler(self, event)
end
return true
else
return UI.SlideOut.eventHandler(self, event)
end
return true
end
--[[-- Startup logic --]]--