Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[ gedit
- advanced editor with undo, page views with word wrap, keyboard shortcuts, and
- advanced printing functions (coming soon)
- based on edit by dan200+cloudy, enhancements by GopherAtl
- Do whatever you want, unless you want to take credit, in which case screw you, jerk.
- --]]
- -- Get file to edit
- local tArgs = { ... }
- if #tArgs == 0 then
- print( "Usage: edit <path>" )
- return
- end
- -- Error checking
- local sPath = shell.resolve( tArgs[1] )
- local bReadOnly = fs.isReadOnly( sPath )
- if fs.exists( sPath ) and fs.isDir( sPath ) then
- print( "Cannot edit a directory." )
- return
- end
- local x,y = 1,1
- local w,h = term.getSize()
- local scrollX, scrollY = 0,0
- local tLinesRaw = {} --raw lines - end in implied \ns
- local tLinesDrawn = {} --modified version for current draw view, with addl. metadata
- local bRunning = true
- local bChanged = false
- local tViewModes = {"code","document","page"}
- local nViewMode = 0
- --undo related
- local tUndo={}
- local tRedo={}
- local hChangeTimer=0
- local sLastChangeType=""
- --conveinence guys
- local function fTrue() return true end
- local function fFalse() return false end
- local function fNil() end
- local function hasChanged() return bChanged end
- -- Menus and other fierce creatures
- local nMenuItem = 1
- local onConfirm=fNil
- local sMenuTitle=""
- local tMenuStack = {}
- --confirmation menu
- local tConfirm = {
- {hotkey="n", sLabel="No", fConfirm=fFalse, onSelect=fNil},
- {hotkey="y", sLabel="Yes", fConfirm=fFalse, onSelect=fNil},
- }
- --forms
- local tActiveForm=nil
- local nSelectedField=1
- local nFieldCursorX=1
- local nFieldCursorY=1
- --printer settings
- local tFormPrintSettings = {
- { sLabel="Tab Size", type="number", value=2,
- x=2, y=3, nLabelWidth=10, nFieldWidth=3 },
- { sLabel="Indent", type="number", value=0,
- x=2, y=4, nLabelWidth=10, nFieldWidth=3 },
- { sLabel="Copies", type="number", value=1,
- x=2, y=5, nLabelWidth=10, nFieldWidth=3 },
- { sLabel="Color", type="boolean", value=false,
- x=2, y=6, nLabelWidth=10, nFieldWidth=3 },
- width=31,height=17,
- x=(w-31)/2, y=(h-17)/2,
- }
- local tPrintSettings={tabSize=2,indent=0,numCopies=1,color=true}
- local sStatus = "Press Ctrl to access menu"
- local function onChange(sType,sChar,x,y)
- bChanged=true
- if sType==sLastChangeType then
- --append to current change
- if sType=="backspace" then --backspace deletes from right to left
- tUndo[#tUndo].sText=sChar..tUndo[#tUndo].sText
- else
- tUndo[#tUndo].sText=tUndo[#tUndo].sText..sChar
- end
- tUndo[#tUndo].nCount=tUndo[#tUndo].nCount+1
- else
- --start new change
- tUndo[#tUndo+1]={sType=sType,sText=sChar,nCount=1,x=x,y=y}
- end
- --any changes obliterate any redo hist..., er, future.
- tRedo={}
- sLastChangeType=sType
- hChangeTimer=os.startTimer(1)
- end
- local function closeChange()
- hChangeTimer=0
- sLastChangeType=""
- end
- local function load(_sPath)
- tLinesRaw = {}
- if fs.exists( _sPath ) then
- local file = io.open( _sPath, "r" )
- local sLine = file:read()
- while sLine do
- table.insert( tLinesRaw, sLine )
- sLine = file:read()
- end
- file:close()
- end
- if #tLinesRaw == 0 then
- table.insert( tLinesRaw, "" )
- end
- end
- local function save( _sPath )
- -- Create intervening folder
- local sDir = sPath:sub(1, sPath:len() - fs.getName(sPath):len() )
- if not fs.exists( sDir ) then
- fs.makeDir( sDir )
- end
- -- Save
- local file = nil
- local function innerSave()
- file = fs.open( _sPath, "w" )
- if file then
- for n, sLine in ipairs( tLinesRaw ) do
- file.write( sLine .. "\n" )
- end
- else
- error( "Failed to open ".._sPath )
- end
- end
- local ok = pcall( innerSave )
- if file then
- file.close()
- end
- return ok
- end
- --======= Drawing functions =======--
- function getWrapCol(str,w)
- --start at w and work back
- for i=w,1,-1 do
- if string.sub(str,i,i)==' ' or string.sub(str,i,i)=='\t' then
- return i
- end
- end
- --whole thing had no breaks, so interrupt word :(
- return w
- end
- function getStrippedLine(rawLine,viewMode)
- local outStr=""
- local i=1
- local wrapW=viewMode==3 and 25 or w
- while i<=#rawLine do
- local ch=string.sub(rawLine,i,i)
- if viewMode==3 and ch=="%" then
- i=i+1
- ch=string.sub(rawLine,i,i)
- if ch=="%" or ch=="" then
- outStr=outStr..ch
- elseif ch=="<" then
- outStr=""
- end
- elseif ch=="\t" then
- local spaces=tFormPrintSettings[1].value-#outStr%tFormPrintSettings[1].value
- outStr=outStr..string.rep(" ",spaces)
- elseif ch=="\n" then
- if i>1 then
- error("somehow a \n got left in a line, should've been split before it got to getStrippedLine!")
- end
- outStr=string.rep(" ",tFormPrintSettings[2].value)
- else
- outStr=outStr..ch
- end
- i=i+1
- end
- local outLines={}
- local offset=0
- local offScr=0
- local i=1
- if viewMode>1 and #outStr>wrapW then
- local nSub=0 --subline number
- while #outStr>wrapW do
- wrapPos=getWrapCol(outStr,wrapW)
- outLines[i]={s=string.sub(outStr,1,wrapPos).."",offsetRaw=offset, offsetScr=offScr}
- offset=offset+wrapPos
- offScr=offScr+#outLines[i].s
- i=i+1
- outStr=string.sub(outStr,wrapPos+1)..""
- end
- end
- outLines[i]={s=outStr,offsetRaw=offset, offsetScr=offScr}
- return outLines
- end
- function scrPosToRaw(x,y,view)
- if view==nil then view=nVieWMode end
- local rawY=tLinesDrawn[y].raw
- local sLine = tLinesRaw[rawY]
- local rawX=scrColToIndex(sLine,tLinesDrawn[y].offsetScr+x,view)
- return rawX,rawY
- end
- function scrColToIndex(rawLine,col,viewMode)
- if viewMode==nil then viewMode=nViewMode end
- local index, curCol=1,1
- while curCol<col and index<=#rawLine do
- local ch=string.sub(rawLine,index,index)
- if ch=="\n" then
- curCol=curCol+tPrintSettings.indent
- elseif ch=="\t" then
- curCol=curCol-(curCol-1)%tPrintSettings.tabSize + tPrintSettings.tabSize
- index=index+1
- elseif viewMode==3 and ch=="%" then
- if #rawLine>index and string.sub(rawLine,index+1,index+1)=="%" then
- curCol=curCol+1
- end
- index=index+2
- else
- index=index+1
- curCol=curCol+1
- end
- end
- if viewMode==3 then
- while index<=#rawLine and string.sub(rawLine,index,index)=="%" do
- if index<#rawLine and string.sub(rawLine,index+1,index+1)=="%" then
- break
- end
- index=index+2
- end
- end
- return index
- end
- function rawToScrPos(rawX,rawY,viewMode)
- if viewMode==nil then viewMode=nViewMode end
- local sLine=tLinesRaw[rawY]
- local x,y=0,1
- --get the initial y for the line rawY
- while rawY>tLinesDrawn[y].raw do
- y=y+1
- end
- --zero in on actual line
- local offset=0
- while y<#tLinesDrawn and tLinesDrawn[y+1].raw==rawY and tLinesDrawn[y+1].offsetRaw<rawX do
- offset=tLinesDrawn[y+1].offsetRaw
- y=y+1
- end
- --loop through raw to find this actual offset
- while offset<rawX do
- local ch=string.sub(sLine,offset,offset)
- if ch=="%" and viewMode==3 then
- if #sLine>offset and string.sub(sLine,offset+1,offset+1)=="%" then
- x=x+1
- end
- offset=offset+2
- elseif ch=="\n" then
- x=x+tPrintSettings.indent
- offset=offset+1
- elseif ch=="\t" then
- x=x-(x-1)%tPrintSettings.tabSize+tPrintSettings.tabSize
- offset=offset+1
- else
- x=x+1
- offset=offset+1
- end
- end
- return x,y
- end
- local function redrawText()
- for y=1,h-1 do
- term.setCursorPos( 1 - scrollX, y )
- term.clearLine()
- local sLine = tLinesDrawn[ y + scrollY ]
- if sLine ~= nil then
- term.write( sLine.s )
- end
- end
- term.setCursorPos( x - scrollX, y - scrollY )
- end
- local function rebuildText()
- tLinesDrawn={}
- local iDrawn=1
- for i,sLine in ipairs(tLinesRaw) do
- --if it fits, just copy it
- local stripped=getStrippedLine(sLine,nViewMode)
- for j=1,#stripped do
- tLinesDrawn[iDrawn]=stripped[j]
- tLinesDrawn[iDrawn].raw=i
- iDrawn=iDrawn+1
- end
- end
- redrawText()
- end
- local function updateFormCursorPos()
- local tField=tActiveForm[nSelectedField]
- if tField.type=="number" or tField.type=="boolean" then
- nFieldCursorX=tField.nFieldWidth
- nFieldCursorY=1
- else
- nFieldCursorX=#tField.value
- end
- end
- local function redrawForm(all)
- --draw the frame
- local x,y=tActiveForm.x,tActiveForm.y
- local width,height=tActiveForm.width,tActiveForm.height
- if all then
- --error("redrawing all,"..x..","..y..")
- term.setCursorPos(x,y)
- term.write("+"..string.rep("-",width-2).."+")
- for i=1,height-2 do
- term.setCursorPos(x,y+i)
- term.write("|"..string.rep(" ",width-2).."|")
- end
- term.setCursorPos(x,y+2)
- term.write("+"..string.rep("-",width-2).."+")
- term.setCursorPos(x,y+height-1)
- term.write("+"..string.rep("-",width-2).."+")
- term.setCursorPos(x+6,y+1)
- term.write("Print preferences")
- end
- --fields and values
- for i=1,#tActiveForm do
- local field=tActiveForm[i]
- term.setCursorPos(x+field.x,y+field.y)
- term.write(field.sLabel)
- term.setCursorPos(x+field.x+field.nLabelWidth,y+field.y)
- --generate value string
- local fieldStr=""
- if field.type=="number" then
- fieldStr=tostring(field.value)
- fieldStr=string.rep(" ",field.nFieldWidth-#fieldStr)..fieldStr
- elseif field.type=="boolean" then
- fieldStr=field.value and "Yes" or "No"
- fieldStr=fieldStr..string.rep(" ",field.nFieldWidth-#fieldStr)
- else
- fieldStr=field.value..string.rep(" ",field.nFieldWidth-#field.value)
- end
- if nSelectedField==i then
- fieldStr="["..fieldStr.."]"
- else
- fieldStr=" "..fieldStr.." "
- end
- term.write(fieldStr)
- end
- --set the cursor position into the active field
- local tField=tActiveForm[nSelectedField]
- term.setCursorPos(
- x+tField.x+tField.nLabelWidth+1+nFieldCursorX,
- y+tField.y+nFieldCursorY-1)
- end
- local function redrawLine(_nY)
- local sLine = tLinesDrawn[_nY].s
- term.setCursorPos( 1 - scrollX, _nY - scrollY )
- term.clearLine()
- term.write( sLine )
- term.setCursorPos( x - scrollX, _nY - scrollY )
- end
- local function redrawRawLine(rawY)
- local sLine=tLinesRaw[rawY]
- local startLine
- for i=1,#tLinesDrawn do
- if tLinesDrawn[i].raw==rawY then
- startLine=i
- break
- end
- end
- local endLine=startLine
- while endLine<#tLinesDrawn and tLinesDrawn[endLine+1].raw==rawY do
- endLine=endLine+1
- end
- local newLines=getStrippedLine(sLine,nViewMode)
- for i=1,#newLines do
- if tLinesDrawn[startLine+i-1]==nil or tLinesDrawn[startLine+i-1].raw~=rawY then
- table.insert(tLinesDrawn,startLine+i-1,newLines[i])
- else
- tLinesDrawn[startLine+i-1]=newLines[i]
- end
- tLinesDrawn[startLine+i-1].raw=rawY
- end
- if endLine-startLine+1 == #newLines then
- for j=startLine,endLine do
- redrawLine(j)
- end
- else
- redrawText()
- end
- end
- local function redrawMenu()
- local prevX,prevY=term.getCursorPos()
- term.setCursorPos( 1, h )
- term.clearLine()
- local sLeft, sRight
- if #tMenuStack>0 then
- local tActiveMenu=tMenuStack[#tMenuStack]
- local sMenu = sMenuTitle
- for n,tItem in ipairs( tActiveMenu ) do
- if n == nMenuItem then
- sMenu = sMenu.."["..tItem.sLabel.."]"
- else
- sMenu = sMenu.." "..tItem.sLabel.." "
- end
- end
- sLeft = sMenu
- else
- sLeft = sStatus
- end
- sRight = "Ln "..y
- -- Left goes last so that it can overwrite the line numbers.
- if sRight then
- term.setCursorPos( w-sRight:len()+1, h )
- term.write(sRight)
- end
- if sLeft then
- term.setCursorPos( 1, h )
- term.write(sLeft)
- end
- -- Cursor highlights selection
- term.setCursorPos( prevX,prevY)
- end
- local function menuStub()
- sStatus = "feature not implemented"
- end
- local function confirmAndDo(item)
- if not item.fConfirm() then
- item.onSelect()
- return true
- else
- tMenuStack[#tMenuStack+1]=tConfirm
- nMenuItem=1
- tConfirm[2].onSelect=item.onSelect
- sMenuTitle=item.sConfirm
- return false
- end
- end
- local function doMenuItem( _n )
- local tMenu=tMenuStack[#tMenuStack]
- local bClosed=true
- if type(tMenu[_n].onSelect)=="table" then
- --open submenu
- tMenuStack[#tMenuStack+1]=tMenu[_n].onSelect
- bClosed=false
- nMenuItem=1
- else
- bClosed=confirmAndDo(tMenu[_n])
- end
- if #tMenuStack>0 and bClosed then
- tMenuStack = {}
- term.setCursorBlink( true )
- end
- redrawMenu()
- end
- local function setCursor( x, y )
- local screenX = x - scrollX
- local screenY = y - scrollY
- local bRedraw = false
- if screenX < 1 then
- scrollX = x - 1
- screenX = 1
- bRedraw = true
- elseif screenX > w then
- scrollX = x - w
- screenX = w
- bRedraw = true
- end
- if screenY < 1 then
- scrollY = y - 1
- screenY = 1
- bRedraw = true
- elseif screenY > h-1 then
- scrollY = y - (h-1)
- screenY = h-1
- bRedraw = true
- end
- if bRedraw then
- redrawText()
- end
- term.setCursorPos( screenX, screenY )
- -- Statusbar now pertains to menu, it would probably be safe to redraw the menu on every key event.
- redrawMenu()
- end
- local function switchViewMode(nNewMode)
- --convert current cursor pos to raw
- local rawY=tLinesDrawn[y].raw
- local sLine = tLinesRaw[rawY]
- local rawX=scrColToIndex(sLine,tLinesDrawn[y].offsetScr+x,nViewMode)
- if nNewMode==nViewMode then
- return
- end
- nViewMode=nNewMode
- rebuildText()
- --convert raw position back to screen
- x,y=rawToScrPos(rawX,rawY,nViewMode)
- setCursor(x,y)
- end
- -- modification functions - called on keystroke, also by undo/redo
- local function backspace()
- local rawY=tLinesDrawn[y].raw
- local sLine = tLinesRaw[rawY]
- local rawX=scrColToIndex(sLine,tLinesDrawn[y].offsetScr+x,nViewMode)
- local ch=""
- if rawX > 1 then
- -- Remove character
- local sLine = tLinesRaw[rawY]
- local prevRawX=scrColToIndex(sLine,tLinesDrawn[y].offsetScr+x-1,nViewMode)
- ch=string.sub(sLine,prevRawX,rawX-1)
- tLinesRaw[rawY] = string.sub(sLine,1,prevRawX-1) .. string.sub(sLine,rawX)
- redrawRawLine(rawY)
- rawX = prevRawX
- x,y=rawToScrPos(rawX,rawY,nViewMode)
- setCursor( x, y )
- elseif y > 1 then
- -- Remove newline
- local sPrevLen = string.len( tLinesRaw[rawY-1] )
- tLinesRaw[rawY-1] = tLinesRaw[rawY-1] .. tLinesRaw[rawY]
- table.remove( tLinesRaw, rawY )
- rebuildText()
- redrawText()
- ch="\n"
- rawX = sPrevLen + 1
- rawY = rawY - 1
- x,y=rawToScrPos(rawX,rawY,nViewMode)
- setCursor( x, y )
- end
- return ch
- end
- local function delete()
- local rawY=tLinesDrawn[y].raw
- local sLine = tLinesRaw[rawY]
- local rawX=scrColToIndex(sLine,tLinesDrawn[y].offsetScr+x,nViewMode)
- local ch=""
- if rawX < string.len( tLinesRaw[rawY] ) + 1 then
- local sLine = tLinesRaw[rawY]
- ch=string.sub(sLine,rawX,rawY)
- tLinesRaw[rawY] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
- redrawRawLine(rawY)
- elseif y<#tLinesRaw then
- tLinesRaw[rawY] = tLinesRaw[rawY] .. tLinesRaw[rawY+1]
- ch="\n"
- table.remove( tLinesRaw, rawY+1 )
- rebuildText()
- end
- return ch
- end
- local function insert(text, bRight)
- local rawY=tLinesDrawn[y].raw
- local sLine = tLinesRaw[rawY]
- local rawX=scrColToIndex(sLine,tLinesDrawn[y].offsetScr+x,nViewMode)
- tLinesRaw[rawY] = string.sub(sLine,1,rawX-1) .. text .. string.sub(sLine,rawX)
- sLine=tLinesRaw[rawY]
- local i=0
- --rebuild and draw raw line
- redrawRawLine(rawY)
- --update cursor
- if not bRight then
- rawX=rawX+ string.len( text )
- x,y=rawToScrPos(rawX,rawY,nViewMode)
- end
- setCursor( x, y )
- end
- local function newline()
- -- Newline
- local rawY=tLinesDrawn[y].raw
- local sLine = tLinesRaw[rawY]
- local rawX=scrColToIndex(sLine,tLinesDrawn[y].offsetScr+x,nViewMode)
- local _,spaces=string.find(sLine,"^[ ]+")
- if not spaces then
- spaces=0
- end
- tLinesRaw[rawY] = string.sub(sLine,1,rawX-1)
- table.insert( tLinesRaw, rawY+1, string.rep(' ',spaces)..string.sub(sLine,rawX) )
- rebuildText()
- rawX = 1
- rawY = rawY + 1
- x,y=rawToScrPos(rawX,rawY,nViewMode)
- setCursor( x, y )
- end
- --File menu
- function fileSave()
- if bReadOnly then
- sStatus = "Access denied"
- else
- local ok, err = save( sPath )
- if ok then
- sStatus="Saved to "..sPath
- bChanged=false
- else
- sStatus="Error saving to "..sPath
- end
- end
- redrawMenu()
- end
- fileSaveAs=menuStub
- function fileRevert()
- if bChanged then
- load( sPath )
- redrawText()
- sStatus="Reverted to last saved version"
- bChanged=false
- else
- sStatus="No changes to revert"
- end
- end
- fileOpen=menuStub
- --print menu
- function printPrint()
- local sPrinterSide = nil
- for n,sSide in ipairs(rs.getSides()) do
- if peripheral.isPresent(sSide) and peripheral.getType(sSide) == "printer" then
- sPrinterSide = sSide
- break
- end
- end
- if not sPrinterSide then
- sStatus = "No printer attached"
- return
- end
- local nPage = 0
- local sName = fs.getName( sPath )
- local printer = peripheral.wrap(sPrinterSide)
- if printer.getInkLevel() < 1 then
- sStatus = "Printer out of ink"
- return
- elseif printer.getPaperLevel() < 1 then
- sStatus = "Printer out of paper"
- return
- end
- local terminal = {
- getCursorPos = printer.getCursorPos,
- setCursorPos = printer.setCursorPos,
- getSize = printer.getPageSize,
- write = printer.write,
- }
- terminal.scroll = function()
- if nPage == 1 then
- printer.setPageTitle( sName.." (page "..nPage..")" )
- end
- while not printer.newPage() do
- if printer.getInkLevel() < 1 then
- sStatus = "Printer out of ink, please refill"
- elseif printer.getPaperLevel() < 1 then
- sStatus = "Printer out of paper, please refill"
- else
- sStatus = "Printer output tray full, please empty"
- end
- term.restore()
- redrawMenu()
- term.redirect( terminal )
- local timer = os.startTimer(0.5)
- sleep(0.5)
- end
- nPage = nPage + 1
- if nPage == 1 then
- printer.setPageTitle( sName )
- else
- printer.setPageTitle( sName.." (page "..nPage..")" )
- end
- end
- bMenu = false
- term.redirect( terminal )
- local ok, error = pcall( function()
- term.scroll()
- for n, sLine in ipairs( tLinesRaw ) do
- print( sLine )
- end
- end )
- term.restore()
- if not ok then
- print( error )
- end
- while not printer.endPage() do
- sStatus = "Printer output tray full, please empty"
- redrawMenu()
- sleep( 0.5 )
- end
- bMenu = true
- if nPage > 1 then
- sStatus = "Printed "..nPage.." Pages"
- else
- sStatus = "Printed 1 Page"
- end
- redrawMenu()
- end
- printPreview=menuStub
- local function printSettings()
- tActiveForm=tFormPrintSettings
- nSelectedField=1
- updateFormCursorPos()
- redrawForm(true)
- end
- --other main
- function _exit()
- bRunning = false
- end
- function _undo()
- if #tUndo==0 then
- sStatus="Nothing to undo!"
- else
- bChanged=false
- --grab it and move to the redo stack
- local tUndoing=tUndo[#tUndo]
- tUndo[#tUndo]=nil
- tRedo[#tRedo+1]=tUndoing
- --set cursor where it was
- x,y=rawToScrPos(tUndoing.x,tUndoing.y)
- --now undo it
- if tUndoing.sType=="char" then
- --move cursor to the end of the inserted text
- x=x+#tUndoing.sText
- --undo with a backspace!
- for i=1,#tUndoing.sText do
- backspace()
- end
- elseif tUndoing.sType=="backspace" then
- x=x-#tUndoing.sText
- --insert the text that was removed
- insert(tUndoing.sText)
- elseif tUndoing.sType=="delete" then
- --insert the snipped text to the right
- insert(tUndoing.sText,true)
- elseif tUndoing.sType=="backspaceNewline" then
- newline()
- elseif tUndoing.sType=="deleteNewline" then
- newline()
- elseif tUndoing.sType=="newline" then
- x=1
- y=y+1
- backspace()
- end
- sStatus=""
- end
- end
- function _redo()
- if #tRedo==0 then
- sStatus="Nothing to redo!"
- else
- bChanged=false
- --grab it and move to the redo stack
- local tRedoing=tRedo[#tRedo]
- tRedo[#tRedo]=nil
- tUndo[#tUndo+1]=tRedoing
- --set cursor where it was
- x,y=rawToScrPos(tRedoing.x,tRedoing.y)
- --now undo it
- if tRedoing.sType=="char" then
- insert(tRedoing.sText)
- elseif tRedoing.sType=="backspace" then
- --undo with a backspace!
- for i=1,tRedoing.nCount do
- backspace()
- end
- elseif tRedoing.sType=="delete" then
- --undo with a backspace!
- for i=1,tRedoing.nCount do
- delete()
- end
- elseif tRedoing.sType=="backspaceNewline" then
- y=y+1
- x=1
- backspace()
- elseif tRedoing.sType=="deleteNewline" then
- delete()
- elseif tRedoing.sType=="newline" then
- newline()
- end
- sStatus=""
- end
- end
- local sConfirmDiscard="Discard unsaved changes? "
- local tMenuItems = {
- {hotkey="f", sLabel="File", fConfirm=fFalse,
- onSelect={
- {hotkey="s",sLabel="(S)ave", fConfirm=fFalse,onSelect=fileSave},
- {hotkey="a",sLabel="Save (A)s", fConfirm=fFalse,onSelect=fileSaveAs},
- {hotkey="r",sLabel="(R)evert",fConfirm=hasChanged,sConfirm=sConfirmDiscard,onSelect=fileRevert},
- {hotkey="o",sLabel="(O)pen", fConfirm=hasChanged,sConfirm=sConfirmDiscard,onSelect=fileOpen},
- {hotkey="x", sLabel="E(x)it",fConfirm=hasChanged,sConfirm=sConfirmDiscard,onSelect=_exit},
- },
- },
- {hotkey="e", sLabel="Edit", fConfirm=false,
- onSelect={
- {hotkey="z", sLabel="Undo(Z)",fConfirm=fFalse,onSelect=_undo},
- {hotkey="y", sLabel="Redo(Y)",fConfirm=fFalse,onSelect=_redo},
- },
- },
- {hotkey="v", sLabel="View", fConfirm=false,
- onSelect={
- {hotkey="c", sLabel="Code view",fConfirm=fFalse,onSelect=function() switchViewMode(1) end},
- {hotkey="d", sLabel="Document view",fConfirm=fFalse,onSelect=function() switchViewMode(2) end},
- {hotkey="p", sLabel="Page view",fConfirm=fFalse,onSelect=function() switchViewMode(3) end},
- },
- },
- {hotkey="p", sLabel="Print", fConfirm=fFalse,
- onSelect={
- {hotkey="p",sLabel="(P)rint",fConfirm=fFalse,onSelect=printPrint},
- {hotkey="r",sLabel="P(r)eview...",fConfirm=fFalse,onSelect=printPreview},
- {hotkey="s",sLabel="(S)ettings...",fConfirm=fFalse,onSelect=printSettings},
- }
- },
- }
- --table of ctrl-key events handled in the doc view - also handled in menu while not in a form?
- local tDocCtrlKeys={
- one={fConfirm=fFalse,onSelect=function() switchViewMode(1) end},
- two={fConfirm=fFalse,onSelect=function() switchViewMode(2) end},
- three={fConfirm=fFalse,onSelect=function() switchViewMode(3) end},
- r={fConfirm=hasChanged,sConfirm=sConfirmDiscard,onSelect=fileRevert},
- x={fConfirm=hasChanged,sConfirm=sConfirmDiscard,onSelect=_exit},
- s={fConfirm=fFalse,onSelect=fileSave},
- a={fConfirm=fFalse,onSelect=fileSaveAs},
- p={fConfirm=fFales,onSelect=printPrint},
- z={fConfirm=fFalse,onSelect=_undo},
- y={fConfirm=fFalse,onSelect=_redo},
- }
- function docInputHandler(sEvent,param)
- if sEvent == "key" then
- if param == 200 then --up
- closeChange()
- -- Up
- if y > 1 then
- -- Move cursor up
- y = y - 1
- x = math.min( x, string.len( tLinesDrawn[y].s ) + 1 )
- setCursor( x, y )
- end
- elseif param == 208 then --down
- closeChange()
- -- Down
- -- Move cursor down
- if y < #tLinesDrawn then
- y = y + 1
- x = math.min( x, string.len( tLinesDrawn[y].s ) + 1 )
- setCursor( x, y )
- end
- elseif param == 15 then --tab
- -- Tab
- --[[local sLine = tLinesRaw[y]
- -- Indent line
- -- IN CASE OF INSERT TAB IN PLACE:
- -- tLinesRaw[y] = string.sub(sLine,1,x-1) .. " " .. string.sub(sLine,x)
- local nSpaces=2-x%2
- local sSpaces=string.rep(" ",nSpaces)
- local px,py=x,y
- tLinesRaw[y]=sSpaces..tLinesRaw[y]
- x = x+nSpaces
- setCursor( x, y )
- redrawLine(y)
- --]]
- local px,py=scrPosToRaw(x,y)
- insert("\t")
- onChange("char","\t",px,py)
- elseif param == 201 then --pageUp
- closeChange()
- -- Page Up
- -- Move up a page
- local sx,sy=term.getSize()
- y=y-sy-1
- if y<1 then y=1 end
- x = math.min( x, string.len( tLinesDrawn[y].s ) + 1 )
- setCursor( x, y )
- elseif param == 209 then --pageDown
- closeChange()
- -- Page Down
- -- Move down a page
- local sx,sy=term.getSize()
- if y<#tLinesDrawn-sy-1 then
- y = y+sy-1
- else
- y = #tLinesDrawn
- end
- x = math.min( x, string.len( tLinesDrawn[y].s ) + 1 )
- setCursor( x, y )
- elseif param == 199 then --home
- closeChange()
- -- Home
- -- [TODO] respect indentation here
- -- Move cursor to the beginning
- x=1
- setCursor(x,y)
- elseif param == 207 then --end
- closeChange()
- -- End
- -- Move cursor to the end
- x = string.len( tLinesDrawn[y].s ) + 1
- setCursor(x,y)
- elseif param == 203 then --left
- closeChange()
- -- Left
- if x > 1 then
- -- Move cursor left
- x = x - 1
- elseif x==1 and y>1 then
- x = string.len( tLinesDrawn[y-1].s ) + 1
- y = y - 1
- end
- setCursor( x, y )
- elseif param == 205 then --right
- closeChange()
- -- Right
- if x < string.len( tLinesDrawn[y].s ) + 1 then
- -- Move cursor right
- x = x + 1
- elseif x==string.len( tLinesDrawn[y].s ) + 1 and y<#tLinesDrawn then
- x = 1
- y = y + 1
- end
- setCursor( x, y )
- elseif param == 211 then --delete
- -- Delete
- local px,py=scrPosToRaw(x,y)
- local ch=delete()
- if ch=="\n" then
- closeChange()
- onChange("deleteNewline",ch,px,py)
- closeChange()
- else
- onChange("delete",ch,px,py)
- end
- elseif param == 14 then --backspace
- -- Backspace
- local px,py=scrPosToRaw(x,y)
- local ch=backspace()
- if ch=="\n" then
- closeChange()
- --NOTE this special case uses x,y, not px,py!
- onChange("backspaceNewline","",x,y)
- closeChange()
- else
- onChange("backspace",ch,px,py)
- end
- elseif param == 28 then --enter
- -- Enter
- local px,py=scrPosToRaw(x,y)
- newline()
- onChange("newline","",px,py)
- closeChange() --never group enters together
- elseif param == 29 or param == 157 then --left or right ctrl
- closeChange()
- -- open top level menus
- bMenu = true
- sMenuTitle=""
- tMenuStack[1]=tMenuItems
- term.setCursorBlink( false )
- nMenuItem = 1
- redrawMenu()
- end
- elseif sEvent == "char" then
- -- Input text
- local px,py=scrPosToRaw(x,y)
- if param=="%" and nViewMode==3 then
- insert("%%")
- else
- insert(param)
- end
- onChange("char",param,px,py)
- elseif sEvent == "timer" and param==hChangeTimer then
- closeChange()
- elseif sEvent == "ctrl-char" then
- if tDocCtrlKeys[param] then
- confirmAndDo(tDocCtrlKeys[param])
- redrawMenu()
- end
- end
- end
- function formInputHandler(sEvent,param)
- if sEvent=="key" then
- if param==keys.leftCtrl or param==keys.rightCtrl then
- tActiveForm=nil
- redrawText()
- elseif param==208 then --down
- nSelectedField=nSelectedField+1
- if nSelectedField>#tActiveForm then
- nSelectedField=1
- end
- updateFormCursorPos()
- redrawForm()
- elseif param==200 then --up
- nSelectedField=nSelectedField-1
- if nSelectedField==0 then
- nSelectedField=#tActiveForm
- updateFormCursorPos()
- end
- redrawForm()
- elseif param==14 then --backspace
- local tField=tActiveForm[nSelectedField]
- if tField.type=="number" then
- if tField.value>0 then
- tField.value=math.floor(tField.value/10)
- redrawForm()
- end
- elseif tField.type=="string" then
- if #tField.value>0 then
- tField.value=string.sub(tField.value,1,#tField.value-1)
- nFieldCursorX=nFieldCursorX-1
- redrawForm()
- end
- end
- end
- elseif sEvent=="char" then
- --edit the field's value...fun stuff here
- local tField=tActiveForm[nSelectedField]
- if tField.type=="number" then
- --numbers only
- if param>="0" and param<="9" then
- if #tostring(tField.value)<tField.nFieldWidth then
- tField.value=tField.value*10+tonumber(param)
- redrawForm()
- end
- end
- elseif tField.type=="boolean" then
- if param=="n" or param=="N" then
- tField.value=false
- redrawForm()
- elseif param=="y" or param=="Y" then
- tField.value=true
- redrawForm()
- elseif param==" " then
- tField.value=not tField.value
- redrawForm()
- end
- end
- elseif tField.type=="string" then
- if #tField.value<tField.nFieldWidth then
- tField.value=tField.value..param
- nFieldCursorX=nFieldCursorX+1
- redrawForm()
- end
- end
- end
- function menuInputHandler(sEvent,param)
- if sEvent == "key" then
- if param == 203 then --left
- -- Move menu left
- nMenuItem = nMenuItem - 1
- if nMenuItem < 1 then
- nMenuItem = #tMenuStack[#tMenuStack]
- end
- redrawMenu()
- elseif param == 205 then --right
- -- Move menu right
- nMenuItem = nMenuItem + 1
- if nMenuItem > #tMenuStack[#tMenuStack] then
- nMenuItem = 1
- end
- redrawMenu()
- elseif param == 28 then --enter
- -- Menu selection
- doMenuItem( nMenuItem )
- elseif param == 29 or param == 157 then --left or right control
- -- Menu toggle
- if #tMenuStack>1 then
- tMenuStack[#tMenuStack]=nil
- nMenuItem=1
- else
- tMenuStack={}
- term.setCursorBlink( true )
- end
- redrawMenu()
- end
- elseif sEvent == "char" then
- -- Select menu items
- local tMenu=tMenuStack[#tMenuStack]
- for n,sMenuItem in ipairs( tMenu ) do
- if sMenuItem.hotkey == string.lower(param) then
- doMenuItem( n )
- break
- end
- end
- elseif sEvent == "ctrl-char" then
- -- Menu toggle
- tMenuStack={}
- term.setCursorBlink( true )
- if tActiveForm==nil then
- if tDocCtrlKeys[param] then
- confirmAndDo(tDocCtrlKeys[param])
- end
- end
- redrawMenu()
- end
- end
- --[[
- local line="[%%%1]"
- print("line : \n"..line)
- print("---")
- local lines=getStrippedLine(line,1)
- print("stripped line mode 1: \n123456789012345678901234567890123456789\n"..lines[1].s)
- tLinesRaw={line}
- local stripped=getStrippedLine(line,1)
- tLinesDrawn={{s=stripped[1].s,raw=1,offsetRaw=0,offsetCol=0}}
- local first=nil
- for i=1,#stripped[1].s do
- local sCh=string.sub(line,i,i)
- local ri=scrColToIndex(line,i,1)
- local rCh=string.sub(line,ri,ri)
- --back to index
- local px,py= rawToScrPos(ri,1,3)
- print("col "..i.."=> index "..ri.." => "..px)
- end
- if first then print("first error at "..first) end
- --]]
- ---[[
- -- Actual program functionality begins
- load(sPath)
- nViewMode=1
- rebuildText()
- term.clear()
- term.setCursorPos(x,y)
- term.setCursorBlink( true )
- redrawText()
- redrawMenu()
- --key->char lookup table for keys that map to chars
- local keyToChar={[2]="1",[3]="2",[4]="3",[5]="4",[6]="5",[7]="6",[8]="7",[9]="8",[10]="9",[11]="0",[12]="minus",[13]="equals",[16]="q",[17]="w",[18]="e",[19]="r",[20]="t",[21]="y",[22]="u",[23]="i",[24]="o",[25]="p",[26]="leftBracket",[27]="rightBracket",[30]="a",[31]="s",[32]="d",[33]="f",[34]="g",[35]="h",[36]="j",[37]="k",[38]="l",[39]="semiColon",[40]="apostrophe",[41]="grave",[43]="backslash",[44]="z",[45]="x",[46]="c",[47]="v",[48]="b",[49]="n",[50]="m",[51]="comma",[52]="period",[53]="slash",}
- -- Handle input
- local eventBuffer={}
- while bRunning do
- local function nextEvent(eventBuffer)
- if #eventBuffer>0 then
- local e,p=unpack(eventBuffer[1])
- table.remove(eventBuffer,1)
- return e,p
- else
- return os.pullEvent()
- end
- end
- local sEvent, param = nextEvent(eventBuffer)
- --identify ctrl-char events at top level
- if sEvent=="key" and keyToChar[param]~=nil then
- local tempBuff={}
- local timer=os.startTimer(0)
- while true do
- e2,p2=nextEvent(eventBuffer)
- --if it's a char event, and it matches the original key...
- if e2=="char" and keyToChar[param]==string.lower(p2) then
- tempBuff[#tempBuff+1]={e2,p2}
- break
- elseif e2=="timer" and p2==timer then
- --no matching char event, this is a ctrl-char key!
- local ch=keyToChar[param]
- if ch=="0" then
- ch="zero"
- elseif ch>="1" and ch<="9" then
- ch=({"one","two","three","four","five","six","seven","eight","nine"})[tonumber(ch)]
- end
- os.queueEvent("ctrl-char",ch)
- break
- else
- --anything else we get buffer, so we can use them in order
- tempBuff[#tempBuff+1]={e2,p2}
- end
- end
- --prepend tempBuffer to eventBuffer, again preserving original order
- for i=1,#eventBuffer do
- tempBuff[#tempBuff+i]=eventBuffer[i]
- end
- eventBuffer=tempBuff
- end
- if #tMenuStack>0 then
- menuInputHandler(sEvent,param)
- elseif tActiveForm then
- formInputHandler(sEvent,param)
- else
- docInputHandler(sEvent,param)
- end
- end
- -- Cleanup
- term.clear()
- term.setCursorBlink( false )
- term.setCursorPos( 1, 1 )
- --]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement