more editor work
This commit is contained in:
223
common/edit.lua
223
common/edit.lua
@@ -23,7 +23,6 @@ local mark = { }
|
|||||||
local searchPattern
|
local searchPattern
|
||||||
local undo = { chain = { }, pointer = 0 }
|
local undo = { chain = { }, pointer = 0 }
|
||||||
local complete = { }
|
local complete = { }
|
||||||
local page
|
|
||||||
|
|
||||||
h = h - 1
|
h = h - 1
|
||||||
|
|
||||||
@@ -116,28 +115,31 @@ local keyMapping = {
|
|||||||
[ 'control-r' ] = 'refresh',
|
[ 'control-r' ] = 'refresh',
|
||||||
}
|
}
|
||||||
|
|
||||||
page = UI.Page {
|
local page = UI.Page {
|
||||||
backgroundColor = color.panelColor,
|
backgroundColor = color.panelColor,
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
transitionHint = 'slideLeft',
|
transitionHint = 'slideLeft',
|
||||||
buttons = {
|
buttons = {
|
||||||
{ text = 'File', dropdown = {
|
{ text = 'File', dropdown = {
|
||||||
|
{ text = 'New ', event = 'menu_action', action = 'file_new' },
|
||||||
|
{ text = 'Open ', event = 'menu_action', action = 'file_open' },
|
||||||
|
{ spacer = true },
|
||||||
{ text = 'Save ^s', event = 'menu_action', action = 'save' },
|
{ text = 'Save ^s', event = 'menu_action', action = 'save' },
|
||||||
{ text = 'Save As... ^S', event = 'menu_action', action = 'save_as', noFocus = true },
|
{ text = 'Save As... ^S', event = 'menu_action', action = 'save_as' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{ text = 'Run', event = 'menu_action', action = 'run' },
|
{ text = 'Run', event = 'menu_action', action = 'run' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{ text = 'Quit ^q', event = 'menu_action', action = 'exit', noFocus = true },
|
{ text = 'Quit ^q', event = 'menu_action', action = 'exit' },
|
||||||
} },
|
} },
|
||||||
{ text = 'Edit', dropdown = {
|
{ text = 'Edit', dropdown = {
|
||||||
{ text = 'Cut ^x', event = 'menu_action', action = 'cut' },
|
{ text = 'Cut ^x', event = 'menu_action', action = 'cut' },
|
||||||
{ text = 'Copy ^c', event = 'menu_action', action = 'copy' },
|
{ text = 'Copy ^c', event = 'menu_action', action = 'copy' },
|
||||||
{ text = 'Paste ^V', event = 'menu_action', action = 'paste_internal' },
|
{ text = 'Paste ^V', event = 'menu_action', action = 'paste_internal' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{ text = 'Find... ^f', event = 'menu_action', action = 'find_prompt', noFocus = true },
|
{ text = 'Find... ^f', event = 'menu_action', action = 'find_prompt' },
|
||||||
{ text = 'Find Next ^n', event = 'menu_action', action = 'find_next' },
|
{ text = 'Find Next ^n', event = 'menu_action', action = 'find_next' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{ text = 'Go to line... ^g', event = 'menu_action', action = 'goto_line', noFocus = true },
|
{ text = 'Go to line... ^g', event = 'menu_action', action = 'goto_line' },
|
||||||
{ text = 'Mark all ^a', event = 'menu_action', action = 'mark_all' },
|
{ text = 'Mark all ^a', event = 'menu_action', action = 'mark_all' },
|
||||||
} },
|
} },
|
||||||
},
|
},
|
||||||
@@ -172,10 +174,6 @@ page = UI.Page {
|
|||||||
[ 'enter' ] = 'accept',
|
[ 'enter' ] = 'accept',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
disable = function(self)
|
|
||||||
UI.SlideOut.disable(self)
|
|
||||||
self:setFocus(page.editor)
|
|
||||||
end,
|
|
||||||
show = function(self)
|
show = function(self)
|
||||||
self.lineNo:reset()
|
self.lineNo:reset()
|
||||||
UI.SlideOut.show(self)
|
UI.SlideOut.show(self)
|
||||||
@@ -217,10 +215,6 @@ page = UI.Page {
|
|||||||
[ 'enter' ] = 'accept',
|
[ 'enter' ] = 'accept',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
disable = function(self)
|
|
||||||
UI.SlideOut.disable(self)
|
|
||||||
self:setFocus(page.editor)
|
|
||||||
end,
|
|
||||||
show = function(self)
|
show = function(self)
|
||||||
self.search:markAll()
|
self.search:markAll()
|
||||||
UI.SlideOut.show(self)
|
UI.SlideOut.show(self)
|
||||||
@@ -267,10 +261,6 @@ page = UI.Page {
|
|||||||
[ 'enter' ] = 'accept',
|
[ 'enter' ] = 'accept',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
disable = function(self)
|
|
||||||
UI.SlideOut.disable(self)
|
|
||||||
self:setFocus(page.editor)
|
|
||||||
end,
|
|
||||||
show = function(self)
|
show = function(self)
|
||||||
self.filename.value = fileInfo.abspath
|
self.filename.value = fileInfo.abspath
|
||||||
if self.filename.value then
|
if self.filename.value then
|
||||||
@@ -283,7 +273,7 @@ page = UI.Page {
|
|||||||
if event.type == 'accept' then
|
if event.type == 'accept' then
|
||||||
local text = self.filename.value
|
local text = self.filename.value
|
||||||
if text and #text > 0 then
|
if text and #text > 0 then
|
||||||
actions.save(shell.resolve(text))
|
actions.save('/' .. text)
|
||||||
end
|
end
|
||||||
self:hide()
|
self:hide()
|
||||||
return true
|
return true
|
||||||
@@ -291,7 +281,7 @@ page = UI.Page {
|
|||||||
return UI.SlideOut.eventHandler(self, event)
|
return UI.SlideOut.eventHandler(self, event)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
quit = UI.SlideOut {
|
unsaved = UI.SlideOut {
|
||||||
x = -26, height = 1, y = -2,
|
x = -26, height = 1, y = -2,
|
||||||
noFill = true,
|
noFill = true,
|
||||||
close = UI.Button {
|
close = UI.Button {
|
||||||
@@ -306,13 +296,13 @@ page = UI.Page {
|
|||||||
x = 2,
|
x = 2,
|
||||||
value = 'Save',
|
value = 'Save',
|
||||||
},
|
},
|
||||||
save = UI.Button {
|
yes = UI.Button {
|
||||||
x = 7,
|
x = 7,
|
||||||
text = 'Yes',
|
text = 'Yes',
|
||||||
backgroundColor = color.panelColor,
|
backgroundColor = color.panelColor,
|
||||||
event = 'save_yes',
|
event = 'save_yes',
|
||||||
},
|
},
|
||||||
quit = UI.Button {
|
no = UI.Button {
|
||||||
x = 13,
|
x = 13,
|
||||||
text = 'No',
|
text = 'No',
|
||||||
backgroundColor = color.panelColor,
|
backgroundColor = color.panelColor,
|
||||||
@@ -324,34 +314,52 @@ page = UI.Page {
|
|||||||
backgroundColor = color.panelColor,
|
backgroundColor = color.panelColor,
|
||||||
event = 'save_cancel',
|
event = 'save_cancel',
|
||||||
},
|
},
|
||||||
disable = function(self)
|
show = function(self, action)
|
||||||
UI.SlideOut.disable(self)
|
self.action = action
|
||||||
self:setFocus(page.editor)
|
|
||||||
end,
|
|
||||||
show = function(self)
|
|
||||||
UI.SlideOut.show(self)
|
UI.SlideOut.show(self)
|
||||||
self:addTransition('slideLeft', { easing = 'outBounce' })
|
self:addTransition('slideLeft', { easing = 'outBounce' })
|
||||||
end,
|
end,
|
||||||
eventHandler = function(self, event)
|
eventHandler = function(self, event)
|
||||||
if event.type == 'save_yes' then
|
if event.type == 'save_yes' then
|
||||||
if actions.save() then
|
if actions.save() then
|
||||||
UI:quit()
|
self:hide()
|
||||||
|
actions.process(self.action)
|
||||||
end
|
end
|
||||||
elseif event.type == 'save_no' then
|
elseif event.type == 'save_no' then
|
||||||
UI:quit()
|
actions.process(self.action, true)
|
||||||
|
self:hide()
|
||||||
elseif event.type == 'save_cancel' then
|
elseif event.type == 'save_cancel' then
|
||||||
self:hide()
|
self:hide()
|
||||||
end
|
end
|
||||||
return UI.SlideOut.eventHandler(self, event)
|
return UI.SlideOut.eventHandler(self, event)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
file_open = UI.FileSelect {
|
||||||
|
modal = true,
|
||||||
|
enable = function() end,
|
||||||
|
transitionHint = 'expandUp',
|
||||||
|
show = function(self)
|
||||||
|
UI.FileSelect.enable(self, fs.getDir(fileInfo.abspath))
|
||||||
|
self:focusFirst()
|
||||||
|
self:draw()
|
||||||
|
end,
|
||||||
|
eventHandler = function(self, event)
|
||||||
|
if event.type == 'select_cancel' then
|
||||||
|
self:disable()
|
||||||
|
elseif event.type == 'select_file' then
|
||||||
|
self:disable()
|
||||||
|
actions.process('open', event.file)
|
||||||
|
end
|
||||||
|
return UI.FileSelect.eventHandler(self, event)
|
||||||
|
end,
|
||||||
|
},
|
||||||
editor = UI.Window {
|
editor = UI.Window {
|
||||||
y = 2,
|
y = 2,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
transitionHint = 'slideRight',
|
transitionHint = 'slideRight',
|
||||||
focus = function(self)
|
focus = function(self)
|
||||||
if self.focused then
|
if self.focused then
|
||||||
page.editor:setCursorPos(x - scrollX, y - scrollY)
|
self:setCursorPos(x - scrollX, y - scrollY)
|
||||||
self:setCursorBlink(true)
|
self:setCursorBlink(true)
|
||||||
else
|
else
|
||||||
self:setCursorBlink(false)
|
self:setCursorBlink(false)
|
||||||
@@ -380,6 +388,7 @@ page = UI.Page {
|
|||||||
elseif ie.code == "mouse_click" or
|
elseif ie.code == "mouse_click" or
|
||||||
ie.code == 'mouse_drag' or
|
ie.code == 'mouse_drag' or
|
||||||
--ie.code == 'mouse_up' or
|
--ie.code == 'mouse_up' or
|
||||||
|
ie.code == 'shift-mouse_click' or
|
||||||
ie.code == 'mouse_down' or
|
ie.code == 'mouse_down' or
|
||||||
ie.code == 'mouse_doubleclick' then
|
ie.code == 'mouse_doubleclick' then
|
||||||
|
|
||||||
@@ -405,14 +414,17 @@ page = UI.Page {
|
|||||||
notification = UI.Notification { },
|
notification = UI.Notification { },
|
||||||
enable = function(self)
|
enable = function(self)
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
self:setFocus(page.editor)
|
self:setFocus(self.editor)
|
||||||
|
end,
|
||||||
|
checkFocus = function(self)
|
||||||
|
if not self.focused or not self.focused.enabled then
|
||||||
|
-- if no current focus, set it to the editor
|
||||||
|
self:setFocus(self.editor)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
eventHandler = function(self, event)
|
eventHandler = function(self, event)
|
||||||
if event.type == 'menu_action' then
|
if event.type == 'menu_action' then
|
||||||
actions.process(event.element.action)
|
actions.process(event.element.action)
|
||||||
if not event.element.noFocus then -- hacky
|
|
||||||
self:setFocus(self.editor)
|
|
||||||
end
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
@@ -435,6 +447,10 @@ local function getFileInfo(path)
|
|||||||
fi.isReadOnly = fs.isReadOnly(fi.abspath)
|
fi.isReadOnly = fs.isReadOnly(fi.abspath)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if multishell then
|
||||||
|
multishell.setTitle(multishell.getCurrent(), fs.getName(fi.path))
|
||||||
|
end
|
||||||
|
|
||||||
return fi
|
return fi
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -446,40 +462,6 @@ local function setError(pattern, ...)
|
|||||||
page.notification:error(string.format(pattern, ...))
|
page.notification:error(string.format(pattern, ...))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function load(path)
|
|
||||||
fileInfo = getFileInfo(path)
|
|
||||||
|
|
||||||
tLines = {}
|
|
||||||
if fs.exists(fileInfo.abspath) then
|
|
||||||
local file = io.open(fileInfo.abspath, "r")
|
|
||||||
local sLine = file:read()
|
|
||||||
while sLine do
|
|
||||||
table.insert(tLines, sLine)
|
|
||||||
sLine = file:read()
|
|
||||||
end
|
|
||||||
file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
if #tLines == 0 then
|
|
||||||
table.insert(tLines, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
local name = fileInfo.path
|
|
||||||
if fileInfo.isNew then
|
|
||||||
if not fileInfo.dirExists then
|
|
||||||
setStatus('"%s" [New DIRECTORY]', name)
|
|
||||||
else
|
|
||||||
setStatus('"%s" [New File]', name)
|
|
||||||
end
|
|
||||||
elseif fileInfo.isReadOnly then
|
|
||||||
setStatus('"%s" [readonly] %dL, %dC',
|
|
||||||
name, #tLines, fs.getSize(fileInfo.abspath))
|
|
||||||
else
|
|
||||||
setStatus('"%s" %dL, %dC',
|
|
||||||
name, #tLines, fs.getSize(fileInfo.abspath))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function save( _sPath )
|
local function save( _sPath )
|
||||||
-- Create intervening folder
|
-- Create intervening folder
|
||||||
local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() )
|
local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() )
|
||||||
@@ -758,6 +740,76 @@ actions = {
|
|||||||
page.search:show()
|
page.search:show()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
file_open = function(force)
|
||||||
|
if not force and undo.chain[#undo.chain] ~= lastSave then
|
||||||
|
page.unsaved:show('file_open')
|
||||||
|
else
|
||||||
|
page.file_open:show('file_open')
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
file_new = function(force)
|
||||||
|
if not force and undo.chain[#undo.chain] ~= lastSave then
|
||||||
|
page.unsaved:show('file_new')
|
||||||
|
else
|
||||||
|
actions.open('/untitled.txt')
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
open = function(filename)
|
||||||
|
if not actions.load(filename) then
|
||||||
|
setError('Unable to load file')
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
load = function(path)
|
||||||
|
if fs.exists(path) and fs.isDir(path) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
fileInfo = getFileInfo(path)
|
||||||
|
|
||||||
|
x, y = 1, 1
|
||||||
|
scrollX, scrollY = 0, 0
|
||||||
|
lastPos = { x = 1, y = 1 }
|
||||||
|
lastSave = nil
|
||||||
|
dirty = { y = 1, ey = h }
|
||||||
|
mark = { }
|
||||||
|
undo = { chain = { }, pointer = 0 }
|
||||||
|
complete = { }
|
||||||
|
|
||||||
|
tLines = { }
|
||||||
|
if fs.exists(fileInfo.abspath) then
|
||||||
|
local file = io.open(fileInfo.abspath, "r")
|
||||||
|
local sLine = file:read()
|
||||||
|
while sLine do
|
||||||
|
table.insert(tLines, sLine)
|
||||||
|
sLine = file:read()
|
||||||
|
end
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
if #tLines == 0 then
|
||||||
|
table.insert(tLines, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = fileInfo.path
|
||||||
|
if fileInfo.isNew then
|
||||||
|
if not fileInfo.dirExists then
|
||||||
|
setStatus('"%s" [New DIRECTORY]', name)
|
||||||
|
else
|
||||||
|
setStatus('"%s" [New File]', name)
|
||||||
|
end
|
||||||
|
elseif fileInfo.isReadOnly then
|
||||||
|
setStatus('"%s" [readonly] %dL, %dC',
|
||||||
|
name, #tLines, fs.getSize(fileInfo.abspath))
|
||||||
|
else
|
||||||
|
setStatus('"%s" %dL, %dC',
|
||||||
|
name, #tLines, fs.getSize(fileInfo.abspath))
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
|
||||||
save = function(filename)
|
save = function(filename)
|
||||||
filename = filename or fileInfo.abspath
|
filename = filename or fileInfo.abspath
|
||||||
if fs.isReadOnly(filename) then
|
if fs.isReadOnly(filename) then
|
||||||
@@ -767,9 +819,6 @@ actions = {
|
|||||||
if ok then
|
if ok then
|
||||||
lastSave = undo.chain[#undo.chain]
|
lastSave = undo.chain[#undo.chain]
|
||||||
fileInfo = getFileInfo(filename)
|
fileInfo = getFileInfo(filename)
|
||||||
if multishell then
|
|
||||||
multishell.setTitle(multishell.getCurrent(), fileInfo.path)
|
|
||||||
end
|
|
||||||
setStatus('"%s" %dL, %dC written',
|
setStatus('"%s" %dL, %dC written',
|
||||||
fileInfo.path, #tLines, fs.getSize(fileInfo.abspath))
|
fileInfo.path, #tLines, fs.getSize(fileInfo.abspath))
|
||||||
return true
|
return true
|
||||||
@@ -783,9 +832,9 @@ actions = {
|
|||||||
page.save_as:show()
|
page.save_as:show()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
exit = function()
|
exit = function(force)
|
||||||
if undo.chain[#undo.chain] ~= lastSave then
|
if not force and undo.chain[#undo.chain] ~= lastSave then
|
||||||
page.quit:show()
|
page.unsaved:show('exit')
|
||||||
else
|
else
|
||||||
UI:quit()
|
UI:quit()
|
||||||
end
|
end
|
||||||
@@ -810,12 +859,9 @@ actions = {
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
status = function()
|
status = function()
|
||||||
local modified = ''
|
local modified = undo.chain[#undo.chain] == lastSave and '' or '[Modified] '
|
||||||
if undo.chain[1] then
|
|
||||||
modified = '[Modified] '
|
|
||||||
end
|
|
||||||
setStatus('"%s" %s%d lines --%d%%--',
|
setStatus('"%s" %s%d lines --%d%%--',
|
||||||
fileInfo.path, modified, #tLines,
|
fileInfo.abspath, modified, #tLines,
|
||||||
math.floor((y - 1) / (#tLines - 1) * 100))
|
math.floor((y - 1) / (#tLines - 1) * 100))
|
||||||
end,
|
end,
|
||||||
|
|
||||||
@@ -1299,23 +1345,10 @@ actions = {
|
|||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
local tArgs = { ... }
|
local args = { ... }
|
||||||
if #tArgs == 0 then
|
if not actions.load(args[1] and args[1] or 'untitled.txt') then
|
||||||
error( "Usage: edit <path>" )
|
error('Error opening file')
|
||||||
end
|
|
||||||
|
|
||||||
-- Error checking
|
|
||||||
local sPath = shell.resolve(tArgs[1])
|
|
||||||
if fs.exists(sPath) and fs.isDir(sPath) then
|
|
||||||
error( "Cannot edit a directory." )
|
|
||||||
end
|
|
||||||
|
|
||||||
load(tArgs[1])
|
|
||||||
|
|
||||||
if multishell then
|
|
||||||
multishell.setTitle(multishell.getCurrent(), fs.getName(sPath))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
UI:start()
|
UI:start()
|
||||||
|
|
||||||
|
|||||||
@@ -65,4 +65,12 @@
|
|||||||
run = "SoundPlayer",
|
run = "SoundPlayer",
|
||||||
iconExt = "\030 \031 \128\0307\159\129\030 \0317\149\0310\144\0300\031 \155\030 \0310\137\144\010\0307\0317\128\128\128\030 \149\0300\031 \149\030 \128\0310\149\0300\031 \149\010\030 \031 \128\0317\130\0307\031 \144\030 \0317\149\0310\129\134\152\129",
|
iconExt = "\030 \031 \128\0307\159\129\030 \0317\149\0310\144\0300\031 \155\030 \0310\137\144\010\0307\0317\128\128\128\030 \149\0300\031 \149\030 \128\0310\149\0300\031 \149\010\030 \031 \128\0317\130\0307\031 \144\030 \0317\149\0310\129\134\152\129",
|
||||||
},
|
},
|
||||||
|
[ "464c4ffd019e1e9691dcf0537c797353ef2b1c1d4833d3d463e5b74ae4547344" ] = {
|
||||||
|
title = "Editor",
|
||||||
|
category = "Apps",
|
||||||
|
run = "edit",
|
||||||
|
iconExt = "7\
|
||||||
|
¨¨¨¨f0¨\
|
||||||
|
¨¨f0¨¨f",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user