Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- WYSIwrite
- A WYSIWYG text editor for printing documents in ComputerCraft
- Written by: Nitrogen Fingers
- ]]--
- doc = {}
- --The width and height of the screen
- local w,h = term.getSize()
- --Whether or not the program is still running
- local finished = false
- --The length allowed for an individual line
- local linelen = 25
- --The lines allowed for an individual page
- local pagelen = 21
- --The currently viewed page
- local currpage = 1
- --The currently edited line
- local currline = 1
- --The currently index of the line edit
- local currind = 1
- --The amount of scrolling applied to the page on the Y.
- local yscroll = 0
- --The drawing offset
- local _xoff,_yoff = math.floor(w/2 - linelen/2),4
- --Whether or not a scroll or page change has occurred
- local lscroll = true
- --Whether or not the player is currently in a drop down menu
- local inDropDown = false
- --Whether or not whitespace characters are displayed
- local whitespaceVis = false
- --Whether or not a change has been made to the document since last save
- local sChange = false
- --The path currently being used to save to
- local sPath = nil
- --The file's name
- local sName = nil
- --For me
- local testMode = false
- local _tro = false
- function testPrint(_str)
- if testMode then
- term.setBackgroundColour(colours.white)
- term.setTextColour(colours.red)
- print(_str)
- lscroll = true
- _tro = true
- end
- end
- function testWait()
- if testMode and _tro then os.pullEvent("key") end
- _tro = false
- end
- --[[ Constructs an empty document that can be edited. Empty documents contain a
- single page, with one line that cannot be deleted.
- Parameters: none
- Returns: none
- ]]--
- local function constructNewDocument(_sName, _sPath)
- --pages
- doc = {
- --lines
- {
- ""
- }
- }
- currpage = 1
- currline = 1
- currind = 1
- paginateOverflow()
- sPath = _sPath
- sName = _sName
- sChange = false
- end
- --[[ Constructs a document from an existing source
- Parameters: none
- Returns: none
- ]]--
- local function loadDocument(_spath)
- doc = {
- { }
- }
- assert(fs.exists(_spath), _spath)
- local _file = fs.open(_spath, "r")
- local _line = _file.readLine()
- while _line do
- if #doc[#doc] < pagelen then
- table.insert(doc[#doc], _line.."\n")
- else
- table.insert(doc, { })
- table.insert(doc[#doc], _line.."\n")
- end
- currpage = #doc
- currline = #doc[#doc]
- --Sneaky newlines
- _ol = _line
- _line = _file.readLine()
- if not _line then
- doc[#doc][#doc[#doc]] = _ol
- end
- paginateOverflow()
- end
- currpage = 1
- currline = 1
- currind = 1
- _file:close()
- sChange = false
- end
- --[[ "Depaginates" the current document into straight lines and saves the
- output to a file location for standard viewing
- Parameters: _spath:string= the location to save the file
- Returns: none
- ]]--
- local function saveDocument()
- local _lines = { "" }
- local _opi,_oli = currpage, currline
- currpage = 1
- currline = 0
- while nextLine() do
- local _cline = doc[currpage][currline]
- if _cline:sub(#_cline) == "\n" then
- _lines[#_lines] = _lines[#_lines].._cline:sub(1,#_cline-1)
- table.insert(_lines, "")
- else
- _lines[#_lines] = _lines[#_lines].._cline
- end
- end
- --Remove that empty 'newline' we include
- if _lines[#_lines] == "" then table.remove(_lines, #_lines) end
- local _file = fs.open(sPath, "w")
- for i=1,#_lines do
- _l = _lines[i]
- _file.writeLine(_l)
- end
- _file.close()
- currpage, currline = _opi, _oli
- sChange = false
- end
- --[[ Updates the document scroll to match the movement of the cursor.
- Parameters: none
- Returns: none
- ]]--
- local function checkYScroll()
- if yscroll + currline + _yoff > h - 3 then
- yscroll = h - 3 - currline - _yoff
- lscroll = true
- elseif yscroll + currline + _yoff < 3 then
- yscroll = 3 - currline - _yoff
- lscroll = true
- end
- end
- --[[ Lowers the current line by 1, if possible
- Parameters: none
- Returns: true if there is a next line; false otherwise
- ]]--
- function nextLine()
- if currline < #doc[currpage] then
- currline = currline + 1
- elseif currpage < #doc then
- currpage = currpage + 1
- currline = 1
- else return false end
- return true
- end
- --[[ Ensures the cursor is in a valid position. Being outside the size of
- a string or before a forbidden character like a newline or tab are fixed.
- Parameters: none
- Returns: none
- ]]--
- local function fixCursorPos()
- if not doc[currpage] then
- currpage = #doc
- currline = #doc[#doc]
- end
- if not doc[currpage][currline] then
- if currline > linelen then
- currline = linelen
- else currline = #doc[currpage] end
- currind = #doc[currpage][currline] + 1
- end
- if currind < 1 then currind = 1
- elseif currind > #doc[currpage][currline] + 1 then
- currind = #doc[currpage][currline] + 1
- end
- if currind > 1 and doc[currpage][currline]:sub(currind-1) == "\n" then
- currind = currind - 1
- end
- end
- --[[ Goes to the previous line, if possible
- Parameters: none
- Returns: true if there is a previous line; false otherwise
- ]]--
- local function lastLine()
- if currline > 1 then
- currline = currline - 1
- elseif currpage > 1 then
- currpage = currpage - 1
- currline = pagelen
- else return false end
- return true
- end
- --[[ Gets the previous line in the document to the one provided
- Parameters: _page: the page in question (default to currpage)
- _line: the line in question (default to currline)
- Returns: string=The previous line, or nil if on the first line
- ]]--
- local function getPrevLine(_page, _line)
- if not _page or not _line then _page = currpage _line = currline end
- if _page == 1 and _line == 1 then return nil
- elseif _line == 1 then _line = pagelen _page = _page - 1
- else _line = _line - 1 end
- return doc[_page][_line]
- end
- --[[ Gets the next line in the document to the one provided
- Parameters: _page: the page in question (default to currpage)
- _line: the line in question (default to currline)
- Returns: string=The next line, or nil if on the last line
- ]]--
- local function getNextLine(_page, _line)
- if not _page or not _line then _page = currpage _line = currline end
- if _page == #doc and _line == #doc[#doc] then return nil
- elseif _line == #doc[_page] then _line = 1 _page = _page + 1
- else _line = _line + 1 end
- return doc[_page][_line]
- end
- --[[ A helper that returns whether or not the input line is the last in the
- document (the last line has a few special rules associated with it)
- Parameters: _page: the page in question (default to currpage)
- _line: the line in question (default to currline)
- Returns: true if the last line in document; false otherwise
- ]]--
- local function isLastLine(_page, _line)
- if not _page or not _line then _page = currpage _line = currline end
- return _page == #doc and _line == #doc[#doc]
- end
- --[[ Adds a sequence of text to the current line and index
- Parameters: val:String = the character to be inserted
- Returns: none
- ]]--
- local function addCharacter(_val)
- local _cltext = doc[currpage][currline]
- if currind > #_cltext then
- _cltext = _cltext.._val
- else
- _cltext = _cltext:sub(1, currind-1).._val.._cltext:sub(currind)
- end
- doc[currpage][currline] = _cltext
- currind = currind + #_val
- paginateOverflow()
- -- Space actions must also be underflowed; they may have broken a word.
- if _val == " " then paginateUnderflow() end
- checkYScroll()
- sChange = true
- end
- --[[ Removes a character from the current line and index
- Parameters: none
- Returns: none
- ]]--
- function deleteCharacter()
- --We first move our cursor to the character we want to delete
- if currind == 1 then
- if currline > 1 then currline = currline - 1
- elseif currpage > 1 then
- currpage = currpage - 1
- currline = pagelen
- else return end
- currind = #doc[currpage][currline]
- else currind = currind - 1 end
- --Then create 2 'snips'; before and after the character
- local _cline = doc[currpage][currline]
- local _fsnip,_asnip = "",""
- if currind > 1 then _fsnip = _cline:sub(1,currind-1) end
- if currind < #_cline then _asnip = _cline:sub(currind+1) end
- --We patch our snips, remove dead lines and do a cleanup
- doc[currpage][currline] = _fsnip.._asnip
- if doc[currpage][currline] == "" then
- _lline = getPrevLine()
- if not _lline or _lline:sub(#_lline) ~= "\n" then
- removeLine(currpage, currline)
- end
- end
- if not doc[currpage] or not doc[currpage][currline] then
- currpage = #doc
- currline = #doc[currpage]
- _cl = #doc[currpage][currline]
- if doc[currpage][currline]:sub(_cl,_cl) ~= "\n" then
- currind = _cl + 1
- end
- end
- --assert(false, currpage..","..currline..","..currind)
- paginateUnderflow()
- fixCursorPos()
- checkYScroll()
- sChange = true
- end
- --[[ Removes an entire line from the document, and shifts all previous
- lines up
- Parameters: pagenum:int = the page the line is on
- linenum:int = the line to remove
- Returns: none
- ]]--
- function removeLine(_pagenum, _linenum)
- _linenum = _linenum + 1
- if _linenum > pagelen then _linenum = 1 _pagenum = _pagenum + 1 end
- local _lp = ""
- --To delete, we copy each line to replace the previous
- while _pagenum <= #doc and _linenum <= #doc[_pagenum] do
- local _cbuff = doc[_pagenum][_linenum]
- if _linenum == 1 then doc[_pagenum-1][pagelen] = _cbuff
- else doc[_pagenum][_linenum-1] = _cbuff end
- if _linenum == pagelen then _linenum = 1 _pagenum = _pagenum + 1
- else _linenum = _linenum + 1 end
- _lp = _cbuff
- end
- --And black out the very last so we know nothing is there.
- _linenum = _linenum - 1
- if (_pagenum == 1 and _linenum == 1) or _lp:sub(#_lp) == "\n" then
- doc[#doc][#doc[#doc]] = ""
- else doc[#doc][#doc[#doc]] = nil end
- if #doc[#doc] == 0 and #doc ~= 1 then
- table.remove(doc, #doc)
- end
- --So long as we still have something in the doc
- if #doc[1] == 0 then doc[1][1] = "" end
- end
- --[[ Inserts a line of text into a specified index. The replaced line and
- all subsequent lines are pushed down the document
- Parameters: _pagenum:int = the page the line is one
- _linenum:int = the line in which to insert
- _text:string = the text to insert. Defaults to a newline.
- ]]--
- function insertLine(_pagenum, _linenum, _text)
- if not _text then _text = "\n" end
- local _pi,_li = #doc, #doc[#doc] + 1
- if _li > linelen then _pi = _pi + 1 _li = 1 end
- if not doc[_pi] then doc[_pi] = { } end
- local _icount = 0
- while _pi > _pagenum or _li > _linenum do
- doc[_pi][_li] = getPrevLine(_pi,_li)
- _li = _li - 1
- if _li == 0 then _li = pagelen _pi = _pi - 1 end
- _icount = _icount + 1
- end
- doc[_pagenum][_linenum] = _text
- end
- --[[ Updates pagination at the point following the trailing line from the
- cursor for underflow as a result of a delete edit action.
- The pagination is optimized to only go so far as it has to when making
- changes, but can cover the entire document through a switch.
- Parameters: _cflag:bool= set to true to check entire document
- Returns: none
- ]]--
- function paginateUnderflow(_cflag)
- --Whether or not we need to keep paginating
- local _pfollow = false
- --Whether or not a vulnerable underflow occurred. These are characterized
- --as underflows that leave a partial word behind.
- local _vocc = false
- --How many pagination checks we need to run. It's a minimum of 2
- --if we're on the same line
- local _pcount = 3
- --The line we start at on this page
- local _pi,_nli = currpage, currline
- --Our comparataor line
- local _cpline = doc[_pi][_nli]
- --The last character of the second to last line
- local _llsnip = nil
- --Where do we start, here or next line?
- if _pi == 1 and _nli == 1 then _nli = _nli + 1 _pcount = _pcount - 1
- elseif _nli == 1 then _cpline = doc[_pi-1][pagelen]
- else _cpline = doc[_pi][_nli-1] end
- while _pi <= #doc do
- local _li = _nli
- while _li <= #doc[_pi] do
- _llsnip = _cpline:sub(#_cpline)
- local _cline = doc[_pi][_li]
- local _nsp = -1
- local _conn = 0
- if _cline:sub(#_cline) == "\n" then _conn = -1 end
- --We search for a word that will fit on the previous line
- for _nsi = linelen - #_cpline,1,-1 do
- if _cline:sub(_nsi,_nsi) == " " then
- _nsp = _nsi
- --Because we don't use it here
- _conn = 0
- break
- end
- end
- --Single words short enough can be moved too
- if #_cline+_conn <= linelen - #_cpline then
- _nsp = #_cline+_conn
- --A little exception in the event we leave our cursor behind
- end
- --And if there's no space at the end of the line, it moves back too
- if _nsp == -1 and _cpline:sub(#_cpline) ~= " "
- and _cpline:sub(#_cpline) ~= "\n" then
- _nsp = linelen - #_cpline
- --Also unused here
- _conn = 0
- --This however now requires an overflow check
- _vocc = true
- end
- --If no such word exists, we're done
- --Also, if our comparator line has newline, underflow is disallowed
- --NOTE: Hideous code. This could be done far better.
- if (_nsp == -1 or _cpline:sub(#_cpline) == "\n") then
- if _pi ~= currpage and _li ~= currline and _pcount == 0 then
- _pfollow = true
- break
- end
- else
- --Otherwise we snip our words from the current line,
- local _snip = _cline:sub(1,_nsp-_conn)
- doc[_pi][_li] = _cline:sub(_nsp+1-_conn)
- --then stick the snip on the next line (lazy coding but meh)
- if _li > 1 then doc[_pi][_li-1] = doc[_pi][_li-1].._snip
- else
- doc[_pi-1][pagelen] = doc[_pi-1][pagelen].._snip
- end
- --If we performed a deletion on the cursor line line and the
- --cursor is within the clip, we must move it
- if _pi == currpage and _li == currline and currind <= _nsp-_conn then
- currind = currind + #_cpline
- if _li == 1 then
- currpage = currpage -1
- currline = pagelen
- else
- currline = currline - 1
- end
- end
- end
- --Remove any excess lines
- local _slsnip = getPrevLine(#doc, #doc[#doc])
- _slsnip = _slsnip:sub(#_slsnip)
- if doc[_pi][_li] == "" and not(isLastLine(_pi, _li)
- and _slsnip == "\n") then
- removeLine(_pi,_li)
- _li = _li - 1
- end
- --Update our variables, and we're done here.
- if not doc[_pi] or not doc[_pi][_li] then break end
- _cpline = doc[_pi][_li]
- _li = _li + 1
- _pcount = math.max(_pcount-1, 0)
- end
- if _pfollow then break end
- _nli = 1
- _pi = _pi + 1
- end
- --Unsafe carryovers have to check for overflow pagination.
- if _vocc then
- paginateOverflow()
- end
- end
- --[[ Updates pagination at the point following the cursor for overflow as
- a result of an insertion edit action.
- Parameters: _cflag:bool= set to true to check entire document
- Returns: none
- ]]--
- function paginateOverflow(_cflag)
- --Whether or not we need to keep paginating
- local _pfollow = false
- --The line we start at on this page
- local _pi,_nli = currpage, currline
- --Whether or not we've done an insertion, and need to perform another check
- local _icheck = false
- while _pi <= #doc do
- _li = _nli
- while _li <= #doc[_pi] do
- local _cltext = doc[_pi][_li]
- --First we check to see if it's a carry-on line
- local _conn = 0
- local _nl = getNextLine(_pi,_li)
- if _nl and _cltext:sub(#_cltext) ~= " " and
- _nl:sub(1,1) ~= " " then
- _conn = _nl:find(" ")
- if not _conn then _conn = _nl:find("\n") end
- if not _conn then _conn = #_nl
- else _conn = _conn - 1 end
- end
- if _cltext:sub(#_cltext) == "\n" then _conn = -1 end
- --And a bit of leniency for newlines (they don't print anyway)
- --If the line is shorter than the line length, we are done
- if (#_cltext + _conn <= linelen and not _cflag and not _icheck)
- or #_cltext == 0 then
- _pfollow = true
- break
- end
- _icheck = false
- --Otherwise we find the nearest space to the edge of the document
- local _nsp = -1
- for _nsi = linelen, 1, -1 do
- if _cltext:sub(_nsi,_nsi) == " " then
- _nsp = _nsi
- break
- end
- end
- --The excess string is carried to the front of the next line or page
- --Notice the space stays if there is more than one word on that line
- if _nsp == -1 then _nsp = linelen end
- doc[_pi][_li] = doc[_pi][_li]:sub(1,_nsp)
- _carrystr = _cltext:sub(_nsp+1)
- if _li == pagelen then
- if _pi == #doc then
- doc[_pi+1] = { _carrystr }
- --Newlines must be carried, and cannot overflow.
- elseif _carrystr:sub(#_carrystr) == "\n" then
- insertLine(_pi+1,1,_carrystr)
- _icheck = true
- else
- doc[_pi+1][1] = _carrystr..doc[_pi+1][1]
- end
- elseif _li == #doc[_pi] then
- doc[_pi][_li+1] = _carrystr
- elseif _carrystr:sub(#_carrystr) == "\n" then
- insertLine(_pi,_li+1,_carrystr)
- _icheck = true
- else
- doc[_pi][_li+1] = _carrystr..doc[_pi][_li+1]
- end
- --If this pagination affected our cursor, we move down a line
- if _pi == currpage and _li == currline and
- currind > #doc[currpage][currline] then
- currind = currind - #doc[currpage][currline]
- if currline == pagelen then
- currline = 1
- currpage = currpage + 1
- else
- currline = currline + 1
- end
- end
- _li = _li + 1
- end
- if _pfollow then break end
- _nli = 1
- _pi = _pi + 1
- end
- end
- --[[ Draws the 'whitespace' surrounding the page and the menu. This includes both
- page-display features like the margins around the page and borders to make it more
- distinct.
- Parameters: none
- Returns: none
- ]]--
- local function displayClearPage()
- --Drawing the background
- term.setBackgroundColour(colours.lightBlue)
- term.clear()
- term.setBackgroundColour(colours.grey)
- --Adding a little border to the page to make it more distinct
- local _ydpos = _yoff + yscroll - 1
- if _ydpos > 0 and _ydpos <= h then
- term.setCursorPos(_xoff + 1, _ydpos)
- term.write(string.rep(" ", 2 + linelen))
- term.setBackgroundColour(colours.white)
- term.setCursorPos(_xoff, _ydpos + 1)
- term.write(string.rep(" ", 2 + linelen))
- end
- if _ydpos + pagelen + 2 > 0 and _ydpos + pagelen + 2 <= h then
- term.setBackgroundColour(colours.white)
- term.setCursorPos(_xoff, _ydpos + pagelen + 2)
- term.write(string.rep(" ", 2 + linelen))
- end
- for i = math.max(0, _ydpos), math.min(h, pagelen + yscroll + _yoff) do
- if i > _ydpos or i == 0 then
- term.setBackgroundColour(colours.white)
- term.setCursorPos(_xoff, i)
- term.write(" ")
- term.setCursorPos(_xoff + linelen + 1, i)
- term.write(" ")
- else
- term.setCursorPos(_xoff + linelen + 2, i)
- end
- term.setBackgroundColour(colours.grey)
- term.write(" ")
- end
- end
- --[[ A presently mostly empty function that displays tools and information about the
- current document for the user
- Parameters: none
- Returns: none
- ]]--
- local function displayInterface()
- term.setBackgroundColour(colours.lightBlue)
- term.setTextColour(colours.black)
- --A little interface feature to tell the user what page they're viewing
- local _ppos = string.rep(" ",math.floor(math.log10(#doc)) - math.floor(math.log10(currpage)))
- ..currpage.." of "..#doc
- term.setCursorPos(w-#_ppos-1,h)
- term.setTextColour(colours.black)
- term.write(_ppos)
- term.setCursorPos(1,1)
- term.setBackgroundColour(colours.lightGrey)
- if testMode then term.setBackgroundColour(colours.red) end
- term.setTextColour(colours.black)
- term.clearLine()
- term.write(" File")
- local _t = sPath or "Unnamed"
- if #_t > w - 14 then _t = _t:sub(1,#_t-math.max(sName,14)-3).."..."..sName end
- term.setTextColour(colours.grey)
- term.setCursorPos(w/2-#_t/2,1)
- term.write(_t)
- if sChange then
- term.setTextColour(colours.red)
- term.write("*")
- end
- end
- --[[ Draws the currently selected page. This prints exactly the contents of the page and
- whitespace to fill out the gaps as necessary.
- Parameters: page:The page of the document to print
- _out: The output construct
- _x:int = the x offset for the page to be printed at
- _y:int = the y offset for the page to be printed at
- Returns: none
- ]]--
- function printPage(page, _out, _x, _y)
- if not _x or not _y then _x = 1 _y = 0 end
- --Displaying the page itself, both text and no text
- for i=1,pagelen do
- local _ydpos = i + _y
- --This little optimization only works if _out == term, so it's not in for now.
- --if _out == term and _ydpos > 1 and _ydpos <= h then
- _out.setCursorPos(_x, i + _y)
- if i > 0 and i <= #page then
- local _disp = string.gsub(page[i], "\n"," ")
- -- Our whitespace display function
- if (whitespaceVis or testMode) and _out == term then
- for _c=1,#page[i] do
- local _chr = page[i]:sub(_c,_c)
- if _chr == " " then
- term.setTextColour(colours.red)
- term.write(".")
- elseif _chr == "\n" then
- term.setTextColour(colours.red)
- term.write(">")
- else
- term.setTextColour(colours.black)
- term.write(_chr)
- end
- end
- term.setTextColour(colours.black)
- else
- --Bah. This has to change. Forget nice code. This program is one enormous, hideous hack.
- _out.write(_disp)
- end
- _out.write(string.rep(" ", math.max(linelen-#_disp, 0)))
- else
- _out.write(string.rep(" ", linelen))
- end
- -- end
- end
- end
- --[[ Clears and redraws the screen ]]--
- local function clearAndRedraw()
- displayClearPage()
- term.setBackgroundColour(colours.white)
- term.setTextColour(colours.black)
- printPage(doc[currpage], term, _xoff + 1, _yoff + yscroll)
- displayInterface()
- end
- --[[ Draws the WYSIwrite logo. ENORMOUSLY lazy but I'm over this. ]]--
- local function drawLogo(x,y)
- term.setBackgroundColour(colours.grey)
- for i=2,3 do
- term.setCursorPos(x,y+i)
- term.write(string.rep(" ", 5))
- end
- term.setBackgroundColour(colors.white)
- for i=0,3 do if i ~= 2 then
- term.setCursorPos(x + 1, y+i)
- term.write(string.rep(" ",3))
- end end
- term.setCursorPos(x+2,y+1)
- term.write("W")
- end
- --[[ Shows a quick "about" page for those curious ]]--
- local function displayAbout()
- local _dw,_dh = w/2+3,6
- local _l,_t = math.floor(w/2-_dw/2),6
- clearAndRedraw()
- term.setBackgroundColour(colours.lightGrey)
- term.setTextColour(colours.black)
- for _y=0,_dh do
- term.setCursorPos(_l,_t+_y)
- term.write(string.rep(" ", _dw))
- end
- term.setCursorPos(_l + 7, _t + 2)
- term.write("WYSIwrite")
- term.setCursorPos(_l + 7, _t + 4)
- term.write("By Nitrogen Fingers")
- drawLogo(_l + 1, _t + 1)
- os.pullEvent()
- lscroll = true
- end
- --[[ Gets the previous file in the path, if there is one ]]--
- local function getPreviousDir(_path)
- if _path == "" or _path == "/" then return path
- else
- _path = _path:reverse()
- return _path:sub(_path:find("/.*")+1):reverse()
- end
- end
- --[[ Interface malarky ]]--
- --[[ A recycled centre print function I've gotten a LOT of mileage out of
- Parameters: msg:string= the message to display
- height:int= the y position of the start of the message
- width:int= the allowed width of the message
- offset:int= the leftmost border of the message
- Returns:int= the number of lines printed
- ]]--
- local function wprintOffCenter(msg, height, width, offset)
- local inc = 0
- local ops = 1
- while #msg - ops > width do
- local nextspace = 0
- while string.find(msg, " ", ops + nextspace) and
- string.find(msg, " ", ops + nextspace) - ops < width do
- nextspace = string.find(msg, " ", nextspace + ops) + 1 - ops
- end
- local ox,oy = term.getCursorPos()
- term.setCursorPos(width/2 - (nextspace)/2 + offset, height + inc)
- inc = inc + 1
- term.write(string.sub(msg, ops, nextspace + ops - 1))
- ops = ops + nextspace
- end
- term.setCursorPos(width/2 - #string.sub(msg, ops)/2 + offset, height + inc)
- term.write(string.sub(msg, ops))
- return inc + 1
- end
- --[[Produces a nice dropdown menu based on a table of strings. Depending on the position, this will auto-adjust the position
- of the menu drawn, and allows nesting of menus and sub menus. Clicking anywhere outside the menu will cancel and return nothing
- Params: x:int = the x position the menu should be displayed at
- y:int = the y position the menu should be displayed at
- options:table = the list of options available to the user, as strings or submenus (tables of strings, with a name parameter)
- Returns:string the selected menu option.
- ]]--
- local function displayDropDown(x, y, options, noTitle)
- inDropDown = true
- if noTitle then y = y - 1 end
- --Figures out the dimensions of our thing
- local longestX = #options.name
- for i=1,#options do
- local currVal = options[i]
- if type(currVal) == "table" then currVal = currVal.name end
- longestX = math.max(longestX, #currVal)
- end
- local xOffset = math.max(0, longestX - ((w-2) - x) + 1)
- local yOffset = math.max(0, #options - ((h-1) - y))
- local clickTimes = 0
- local tid = nil
- local selection = nil
- while clickTimes < 4 do
- if not noTitle then
- term.setCursorPos(x-xOffset,y-yOffset)
- term.setBackgroundColour(colours.blue)
- term.setTextColour(colours.white)
- term.write(options.name..string.rep(" ", longestX-#options.name + 1))
- end
- for i=1,#options do
- term.setCursorPos(x-xOffset, y-yOffset+i)
- local currVal = options[i]
- if type(currVal.value) == "table" then
- currVal.enabled = #currVal.value > 0
- end
- if i==selection and clickTimes % 2 == 0 then
- term.setBackgroundColour(colours.blue)
- if options[selection].enabled then term.setTextColour(colours.white)
- else term.setTextColour(colours.grey) end
- else
- local _tcol = colours.black
- if not currVal.enabled then _tcol = colours.grey end
- term.setBackgroundColour(colours.lightGrey)
- term.setTextColour(_tcol)
- end
- if type(currVal.value) == "table" then
- term.write(currVal.name..string.rep(" ", longestX-#currVal.name))
- term.setBackgroundColour(colours.blue)
- term.setTextColour(colours.white)
- term.write(">")
- else
- term.write(currVal.name..string.rep(" ", longestX-#currVal.name + 1))
- end
- end
- local id, p1, p2, p3 = os.pullEvent()
- if id == "timer" then
- if p1 == tid then
- clickTimes = clickTimes + 1
- if clickTimes > 2 then
- break
- else
- tid = os.startTimer(0.1)
- end
- end
- elseif id == "key" then
- if p1 == keys.leftCtrl then
- selection = ""
- break
- elseif selection == nil or (p1 == keys.down and selection < #options) then
- selection = selection or 0
- _os = selection
- repeat selection = selection + 1
- until selection == #options + 1 or options[selection].enabled
- --if selection == #options + 1 then selection = _os end
- elseif p1 == keys.up and selection > 1 then
- _os = selection
- repeat selection = selection - 1
- until selection == 0 or options[selection].enabled
- if selection == 0 then selection = _os end
- elseif p1 == keys.enter and options[selection].enabled then
- tid = os.startTimer(0.1)
- clickTimes = clickTimes - 1
- end
- elseif id == "mouse_click" then
- local _xp, _yp = x - xOffset, y - yOffset
- if p2 >= _xp and p2 <= _xp + longestX + 1 and
- p3 >= _yp+1 and p3 <= _yp+#options then
- if options[p3 - _yp].enabled then
- selection = p3-(_yp)
- tid = os.startTimer(0.1)
- end
- else
- selection = ""
- break
- end
- end
- end
- local _val = selection
- if type(selection) == "number" then
- selection = options[selection].value
- end
- if type(selection) == "string" then
- inDropDown = false
- return selection
- elseif type(selection) == "table" then
- return displayDropDown(x + longestX + 1, y + _val, selection, true)
- elseif type(selection) == "function" then
- local _rval = selection()
- if not _rval then return "" else return _rval end
- end
- end
- --[[ Copied out of other menus, simply provides a display to indicate something has happened.
- Parameters: ctitle:string = the title of the dialogue
- msg:string = the message in the dialogue
- arg:{string, colour} = pairs of buttons with strings and their text colour (background is default)
- Returns: none
- ]]--
- local function displayConfirmDialogue(ctitle, msg, ...)
- local _doffX, _doffY = 8, 5
- --We actually print twice- once to get the lines, second time to print proper. Easier this way.
- local lines = wprintOffCenter(msg, _doffY, w - (_doffX+2) * 2, _doffX + 2)
- term.setCursorPos(_doffX, 3)
- term.setBackgroundColour(colours.blue)
- term.setTextColour(colours.white)
- term.write(string.rep(" ", w - _doffX * 2))
- term.setCursorPos(_doffX + (w - _doffX * 2)/2 - #ctitle/2, 3)
- term.write(ctitle)
- term.setTextColour(colours.black)
- term.setBackgroundColour(colours.lightGrey)
- term.setCursorPos(_doffX, 4)
- term.write(string.rep(" ", w - _doffX * 2))
- for i = _doffY, _doffY + lines do
- term.setCursorPos(_doffX, i)
- term.write(" "..string.rep(" ", w - (_doffX) * 2 - 2).." ")
- end
- wprintOffCenter(msg, _doffY, w - (_doffX+2) * 2, _doffX + 2)
- if arg then
- term.setCursorPos(_doffX, _doffY + lines + 1)
- term.write(string.rep(" ", w - _doffX * 2))
- term.setCursorPos(_doffX, _doffY + lines + 2)
- term.write(string.rep(" ", w - _doffX * 2))
- local _sspace = 0
- for _k,_v in ipairs(arg) do _sspace = _sspace + #_v[1] end
- _sspace = (w - (_doffX * 2) - _sspace - (2 * #arg))/(#arg)
- if _sspace <= #arg - 1 then assert(false, "Too little space: needed "..(#arg - 1)..", got ".._sspace) end
- term.setBackgroundColour(colours.grey)
- term.setCursorPos(_doffX + 1, _doffY + lines + 1)
- local _spart = false
- for i=1,#arg do
- _v = arg[i]
- arg[i][3] = term.getCursorPos()
- term.setTextColour(_v[2])
- term.write(" ".._v[1].." ")
- local _vspace = math.floor(_sspace)
- if i >= #arg/2 and not _spart then _vspace = math.ceil(_sspace) _spart = true end
- local _x,_y = term.getCursorPos()
- term.setCursorPos(_x + _vspace, _y)
- end
- end
- --In the event of a message, the player hits anything to continue
- while true do
- local _id,_p1,_p2,_p3 = os.pullEvent()
- if (not arg or #arg == 0) and (id == "key" or id == "mouse_click" or id == "mouse_drag") then
- break
- elseif _id == "key" then
- if _p1 == keys.enter then return 1 end
- elseif _id == "mouse_click" and _p3 == _doffY + lines + 1 then
- for i=1,#arg do
- _v = arg[i]
- if _p2 >= _v[3] and _p2 <= _v[3] + #_v[1] + 1 then return i end
- end
- end
- end
- end
- --[[ Runs a printing interface that lets users select the pages and number of copies to print
- Also displays toner and paper levels, and returns the start, end and copy number, or null.
- ]]--
- local function displayPrint(_printer,_w,_h,_y,_x)
- _w = math.floor(_w)
- if not _x then _x = math.floor(w/2-_w/2) else _x = math.floor(_x) end
- if not _y then _y = math.floor(h/2-_h/2) end
- local _bc,_tc = colours.lightGrey, colours.black
- local _btc,_ttc = colours.blue, colours.white
- local _bfc,_tfc,_tdc = colours.black, colours.white, colours.green
- --The position of the text boxes (which can vary)
- local _cXoff,_cYoff = 0,0
- local _pX,_pY,_cX,_cY = 0,0,0,0
- --The selected pages and copies
- local _pstart, _pend, _copies = 1, #doc, 1
- --The amount of paper and ink required
- local _pageCount = (_pend - _pstart + 1) * _copies
- local _ilX,_ilY = 0,0
- local _wsel = 0
- local function _drawWindow()
- --Window title
- term.setBackgroundColour(_btc)
- term.setTextColour(_ttc)
- term.setCursorPos(_x,_y)
- _title = "Print"
- term.write(string.rep(" ", math.floor(_w/2) - math.floor(#_title/2))
- .._title..string.rep(" ", math.ceil(_w/2) - math.ceil(#_title/2)-1))
- term.setBackgroundColour(colours.red)
- term.setTextColour(colours.white)
- term.write("x")
- --Body and main options
- term.setBackgroundColour(_bc)
- term.setTextColour(_tc)
- for i=1,_h do
- term.setCursorPos(_x,_y+i)
- term.write(string.rep(" ", _w))
- end
- --Hardcoded value. To change.
- local _plen = math.log10(#doc) + 1
- term.setCursorPos(_x + 1, _y + 2)
- term.write ("Print: ")
- _pX,_pY = term.getCursorPos()
- term.setBackgroundColour(colours.white)
- term.write(string.rep(" ", _plen))
- term.setBackgroundColour(_bc)
- term.write("-")
- term.setBackgroundColour(colours.white)
- term.write(string.rep(" ", _plen))
- term.setBackgroundColour(_bc)
- local _ctoken = "Copies: "
- local _clen,_cx,_cy = 2, term.getCursorPos()
- if _cx + #_ctoken + _clen + 2 > _x + _w - 1 then
- _cXoff = 1
- _cYoff = 2
- _ilY = _cy + 4
- term.setCursorPos(_x + _cXoff, _cy + _cYoff)
- else
- _cXoff = _cx + #_ctoken + 3
- _ilY = _cy + 2
- term.write(" ")
- end
- term.write(_ctoken)
- _cX, _cY = term.getCursorPos()
- term.setBackgroundColour(colours.white)
- term.write(" ")
- term.setBackgroundColour(_bc)
- term.setCursorPos(_x + 1, _ilY)
- term.write("Toner level: ")
- term.setCursorPos(_x + 1, _ilY + 1)
- term.write("Paper level: ")
- _ilX = term.getCursorPos()
- --Cancel button
- term.setBackgroundColour(colours.grey)
- term.setTextColour(colours.red)
- term.setCursorPos(_x + _w - 10, _y + _h - 1)
- term.write(" Cancel ")
- end
- local function _drawPrintOptions()
- --Numbers in the pages and copies box
- if _wsel ~= 1 and _pstart == nil then _pstart = 1 end
- if _wsel ~= 2 and _pend == nil then _pend = 1 end
- if _wsel ~= 3 and _copies == nil then _copies = 1 end
- term.setCursorPos(_pX, _pY)
- term.setTextColour(colours.black)
- if _wsel == 1 then term.setBackgroundColour(colours.orange)
- else term.setBackgroundColour(colours.white) end
- if _pstart == nil then term.write(string.rep(" ", math.log10(#doc) + 1))
- else term.write(string.rep(" ", math.floor(math.log10(#doc)) - math.floor(math.log10(_pstart))).._pstart) end
- if _wsel == 2 then term.setBackgroundColour(colours.orange)
- else term.setBackgroundColour(colours.white) end
- term.setCursorPos(term.getCursorPos() + 1, _pY)
- if _pend == nil then term.write(string.rep(" ", math.log10(#doc) + 1))
- else term.write(string.rep(" ", math.floor(math.log10(#doc)) - math.floor(math.log10(_pend))).._pend) end
- if _wsel == 3 then term.setBackgroundColour(colours.orange)
- else term.setBackgroundColour(colours.white) end
- term.setCursorPos(_cX, _cY)
- if _copies == nil then term.write(string.rep(" ", 2))
- else term.write(string.rep(" ", 1 - math.floor(math.log10(_copies))).._copies) end
- --Ink and paper levels
- local _iLvl, _pLvl = _printer.getInkLevel(), _printer.getPaperLevel()
- if not _pstart or not _pend or not _copies then _pageCount = -1
- else _pageCount = (_pend - _pstart + 1) * _copies end
- if _pageCount <= 0 then _pageCount = -1 end
- term.setBackgroundColour(_bc)
- if _iLvl < _pageCount then term.setTextColour(colours.red)
- else term.setTextColour(colours.black) end
- term.setCursorPos(_ilX, _ilY)
- term.write(_iLvl.."")
- term.setTextColour(colours.black)
- if _pageCount == -1 then term.write(" (--)")
- else term.write(" (".._pageCount..")") end
- term.write(string.rep(" ", _x + _w - term.getCursorPos()))
- term.setCursorPos(_ilX, _ilY + 1)
- if _pLvl < _pageCount then term.setTextColour(colours.red)
- else term.setTextColour(colours.black) end
- term.write(_pLvl.."")
- --Print/Invalid button
- term.setCursorPos(_x + 1, _y + _h - 1)
- term.setBackgroundColour(colours.grey)
- if not _pstart or not _pend or _pstart > _pend then
- term.setTextColour(colours.lightGrey)
- term.write(" Invalid ")
- else
- term.setTextColour(colours.green)
- term.write(" Print ")
- term.setBackgroundColour(colours.lightGrey)
- term.write(" ")
- end
- end
- local function _runInput()
- while true do
- local _id, _p1, _p2, _p3 = os.pullEvent()
- if _id == "mouse_click" then
- if _p2 >= _pX and _p2 <= _pX + math.floor(math.log10(#doc)) and _p3 == _pY then
- _wsel = 1
- _pstart = nil
- _drawPrintOptions()
- elseif _p2 >= _pX + math.floor(math.log10(#doc)) + 2 and _p2 <= _pX + (2*math.floor(math.log10(#doc))) + 2 and
- _p3 == _pY then
- _pend = nil
- _wsel = 2
- _drawPrintOptions()
- elseif _p2 >= _cX and _p2 <= _cX + 1 and _p3 == _cY then
- _copies = nil
- _wsel = 3
- _drawPrintOptions()
- elseif _p2 >= _x + 1 and _p2 <= _x + 8 and _p3 == _y + _h - 1 and _pstart <= _pend then
- if _pstart == nil then _pstart = 1 end
- if _pend == nil then _pend = #doc end
- if _copies == nil then _copies = 1 end
- return true
- elseif (_p2 >= _x + _w - 9 and _p2 <= _x + _w - 1 and _p3 == _y + _h - 1) or
- (_p2 == _x + _w - 1 and _p3 == _y) then
- return false
- else
- _wsel = 0
- _drawPrintOptions()
- end
- elseif _id == "key" then
- if _p1 == keys.delete or _p1 == keys.backspace then
- if _wsel == 1 and _pstart then _pstart = math.floor(_pstart / 10) if _pstart == 0 then _pstart = nil end
- elseif _wsel == 2 and _pend then _pend = math.floor(_pend / 10) if _pend == 0 then _pend = nil end
- elseif _wsel == 3 and _copies then _copies = math.floor(_copies / 10) if _copies == 0 then _copies = nil end
- end
- _drawPrintOptions()
- elseif _p1 == keys.tab then
- _wsel = (_wsel % 3) + 1
- _drawPrintOptions()
- elseif _p1 == keys.enter and _pstart <= _pend then
- if _pstart == nil then _pstart = 1 end
- if _pend == nil then _pend = #doc end
- if _copies == nil then _copies = 1 end
- return true
- end
- elseif _id == "char" and _wsel > 0 and tonumber(_p1) then
- if _wsel == 1 then
- if not _pstart then _pstart = 0 end
- _pstart = (10 * _pstart) + tonumber(_p1)
- if _pstart > #doc then _pstart = #doc
- elseif _pstart == 0 then _pstart = nil end
- _drawPrintOptions()
- elseif _wsel == 2 then
- if not _pend then _pend = 0 end
- _pend = (10 * _pend) + tonumber(_p1)
- if _pend > #doc then _pend = #doc
- elseif _pend == 0 then _pend = nil end
- _drawPrintOptions()
- elseif _wsel == 3 then
- if not _copies then _copies = 0 end
- _copies = (10 * _copies) + tonumber(_p1)
- if _copies > 99 then _copies = 99
- elseif _copies == 0 then _copies = nil end
- _drawPrintOptions()
- end
- end
- end
- end
- _drawWindow()
- _drawPrintOptions()
- if _runInput() then return _pstart, _pend, _copies end
- end
- --[[ A more elaborate save menu than my first effort. Directory navigation and
- selection. It works well enough.
- ]]--
- local function displayFileBrowser(_flag,_h,_w,_y,_x)
- local _mt = { ["-s"] = "Save As", ["-b"] = "Browse Files", ["-l"] = "Load File" }
- if not _h then _h = math.floor(h/2) else _h = math.floor(_h) end
- if not _w then _w = math.floor(w/2) else _w = math.floor(_w) end
- if not _x then _x = math.floor(w/2-_w/2) + 1 else _x = math.floor(_x) end
- if not _y then _y = math.floor(h/2-_h/2) end
- local _bc,_tc = colours.lightGrey, colours.black
- local _btc,_ttc = colours.blue, colours.white
- local _bfc,_tfc,_tdc = colours.black, colours.white, colours.green
- --This is a nasty timesaver.
- local _cpath = shell.resolve(sPath or ".")
- if not _cpath:find("/") then _cpath = ""
- elseif sPath then _cpath = "/"..getPreviousDir(_cpath) end
- local _rlist = fs.list(_cpath)
- if _cpath ~= "/" and _cpath ~= "" then table.insert(_rlist, 1, "..") end
- local _scr = 0
- local _fname = ""
- local _abmsg = { ["-l"] = " Open ", ["-s"] = " Save As ",
- [1] = " Invalid ", [2] = " Overwrite " }
- local _labmsg = ""
- local _winh = _h - 7
- local _cpos = 0
- clearAndRedraw()
- --Some dedicated internal draw functions (to speed things up)
- local function _drawWindow()
- --Permanent parts of the window
- term.setBackgroundColour(_btc)
- term.setTextColour(_ttc)
- term.setCursorPos(_x,_y)
- term.write(string.rep(" ", math.floor(_w/2) - math.floor(#_mt[_flag]/2))
- .._mt[_flag]..string.rep(" ", math.ceil(_w/2) -
- math.ceil(#_mt[_flag]/2)-1))
- term.setBackgroundColour(colours.red)
- term.setTextColour(colours.white)
- term.write("x")
- term.setBackgroundColour(_bc)
- term.setTextColour(_tc)
- for i=1,_h do
- term.setCursorPos(_x,_y+i)
- term.write(string.rep(" ", _w))
- end
- term.setBackgroundColour(colours.grey)
- term.setTextColour(colours.red)
- term.setCursorPos(_x + _w - 10, _y + _h - 1)
- term.write(" Cancel ")
- end
- local function _drawBrowser()
- term.setBackgroundColour(_bc)
- term.setTextColour(_tc)
- local _dpath = _cpath
- if #_dpath > _w-4 then
- while _dpath:find("/") do
- local _ind = _dpath:find("/") + 1
- _dpath = _dpath:sub(_ind)
- if #_dpath < _w-7 then
- _dpath = "...".._dpath
- break
- end
- end
- end
- if #_dpath > _w-4 then _dpath = "...".._dpath:sub(_w-4-#_dpath) end
- term.setCursorPos(_x+2,_y+2)
- term.write(_dpath..string.rep(" ", _w-4-#_dpath))
- term.setBackgroundColour(_bfc)
- for i = 1 + _scr, _winh + _scr do
- _pth = _rlist[i] or ""
- term.setCursorPos(_x + 2, _y + 2 + i - _scr)
- if fs.isDir(_cpath.."/".._pth) then
- term.setTextColour(_tdc)
- else term.setTextColour(_tfc) end
- term.write(" ".._pth..string.rep(" ", _w - 5 - #_pth))
- end
- end
- local function _drawActivationButton()
- _owrite = 0
- local _val = _cpath.."/".._fname
- if (not fs.exists(_val) and _flag == "-l") or
- (fs.exists(_val) and fs.isDir(_val)) then
- _owrite = 1
- elseif fs.exists(_val) and _flag == "-s" then _owrite = 2 end
- term.setBackgroundColour(colours.grey)
- term.setCursorPos(_x + 2, _y + _h - 1)
- if _owrite == 1 and _flag ~= "-b" then
- term.setTextColour(colours.lightGrey)
- _labmsg = " Invalid "
- elseif _owrite == 2 then
- term.setTextColour(colours.orange)
- _labmsg = " Overwrite "
- elseif _flag == "-s" then
- term.setTextColour(colours.green)
- _labmsg = " Save "
- elseif _flag == "-l" then
- term.setTextColour(colours.green)
- _labmsg = " Open "
- end
- term.write(_labmsg)
- term.setBackgroundColour(_bc)
- term.write(string.rep(" ", #" Overwrite " - #_labmsg))
- end
- local function _drawWriteBar()
- term.setCursorPos(_x + 2, _y + _h - 3)
- term.setTextColour(colours.black)
- term.setBackgroundColour(colours.lightGrey)
- local _pm = "Save As "
- if _flag == "-l" then _pm = "Open " end
- term.write(_pm)
- local _msg = _fname
- if #_msg > _w - 8 - #_pm then _msg = _msg:sub(#_msg - (_w - 8 - #_pm)) end
- term.setBackgroundColour(colours.white)
- term.write(" ".._msg)
- _cpos = term.getCursorPos()
- term.write(string.rep(" ", math.max(_w - 5 - #_pm - #_msg, 1)))
- end
- _drawWindow()
- _drawActivationButton()
- _drawBrowser()
- _drawWriteBar()
- while true do
- term.setTextColour(colours.black)
- term.setCursorPos(_cpos, _y + _h - 3)
- term.setCursorBlink(true)
- local _id,_p1,_p2,_p3 = os.pullEvent()
- if _id == "key" then
- if _p1 == keys.backspace and #_fname > 0 then
- _fname = _fname:sub(1,#_fname-1)
- _drawActivationButton()
- _drawWriteBar()
- elseif _p1 == keys.up and _scr > 0 then
- _scr = _scr - 1
- _drawBrowser()
- elseif _p1 == keys.down and _scr < #_rlist - _winh then
- _scr = _scr + 1
- _drawBrowser()
- elseif _p1 == keys.enter then
- local _val = _cpath.."/".._fname
- if (_flag == "-l" and fs.exists(_val) and not fs.isDir(_val))
- or(_flag == "-s" and not fs.isDir(_val)) then
- break
- end
- elseif _p1 == keys.leftCtrl or _p == keys.rightCtrl then
- _fname = nil
- break
- end
- elseif _id == "char" then
- _fname = _fname.._p1
- _drawActivationButton()
- _drawWriteBar()
- elseif _id == "mouse_click" then
- if _p2 == _x + _w - 1 and _p3 == _y then
- _fname = nil
- break
- elseif _p2 >= _x + 2 and _p2 <= _x + _w - 2 and _p3 >= _y + 3 and
- _p3 < _y + 3 + _winh then
- _p3 = _p3 - _y - 2
- local _val = _rlist[_p3 + _scr]
- if _val == ".." then
- --Wow why is there no reverse find
- _cpath = getPreviousDir(_cpath)
- _rlist = fs.list(_cpath)
- if _cpath ~= "/" and _cpath ~= "" then table.insert(_rlist, 1, "..") end
- _scr = 0
- _drawBrowser()
- _drawActivationButton()
- elseif fs.isDir(_cpath.."/".._val) then
- _cpath = _cpath.."/".._val
- _rlist = fs.list(_cpath)
- if _cpath ~= "/" and _cpath ~= "" then table.insert(_rlist, 1, "..") end
- _scr = 0
- _drawBrowser()
- _drawActivationButton()
- else
- _fname = _val or _fname
- _drawActivationButton()
- _drawWriteBar()
- end
- elseif _p3 == _y + _h - 1 and _p2 >= _x + 2 and _p2 < _x + 2 + # _labmsg and _labmsg ~= _abmsg[1] then
- break
- elseif _p3 == _y + _h - 1 and _p2 >= _x + _w - 2 - #" Cancel " and
- _p2 < _x + _w - 2 then
- _fname = nil
- return false
- end
- elseif _id == "mouse_scroll" then
- _scr = math.min(_scr, #_rlist - _winh - 1)
- _scr = math.max(_scr + _p1, 0)
- _drawBrowser()
- end
- end
- if _fname then return (_cpath.."/".._fname):sub(2),_fname else return nil end
- end
- local function menuSaveAs()
- local _sp,sn = displayFileBrowser("-s", 14, 24, 3)
- if _sp then
- sPath = _sp
- sName = _sn
- saveDocument()
- end
- end
- local function checkSave()
- if not sChange then return true end
- clearAndRedraw()
- local _val = displayConfirmDialogue("Unsaved Changes", "Your document contains unsaved changes. Are you sure you want to quit?", { [1] = "Save"; [2] = colours.green }, { [1] = "Discard"; [2] = colors.orange; }, { [1] = "Cancel";
- [2] = colors.red; })
- if _val == 1 then
- if sPath then saveDocument()
- else menuSaveAs() end
- return true
- elseif _val == 2 then
- return true
- else
- return false
- end
- end
- --[[
- Menu Bindings, accessed through drop-down menus (keyboard navigable)
- ]]--
- local _printers = { name = "Print"}
- local _mChoices = { name = "File",
- { name = "New File", enabled = true, value = function() if checkSave() then constructNewDocument() end end },
- { name = "Open", enabled = true, value = function()
- if checkSave() then
- local _sp,_sn = displayFileBrowser("-l", 14, 24, 3)
- if _sp then
- loadDocument(_sp)
- sPath = _sp
- sName = _sn
- end end end },
- { name = "Save", enabled = false, value = saveDocument },
- { name = "Save As", enabled = true, value = menuSaveAs },
- { name = "Print", enabled = false, value = _printers },
- { name = "About", enabled = true, value = displayAbout },
- { name = "Quit", enabled = true, value = function() if checkSave() then finished = true end end }
- }
- --[[ Updates the list of currently available printers and other menu options
- Params: none
- Returns: none
- ]]--
- local function updateMenu()
- --Again, horrible. My coding sucks.
- for i=1,6 do _printers[i] = nil end
- for _,_s in pairs(rs.getSides()) do
- if peripheral.isPresent(_s) and peripheral.getType(_s) == "printer" then
- table.insert(_printers, { name = _s, enabled = true, value = _s })
- end
- end
- _mChoices[3].enabled = sChange and sPath
- end
- --Performs the menu
- local function performMenu()
- updateMenu()
- local _val = displayDropDown(2, 1, _mChoices)
- if _val == "New File" then
- constructNewDocument()
- elseif _val == "Quit" then
- finished = true
- elseif peripheral.getType(_val) == "printer" then
- _p = peripheral.wrap(_val)
- clearAndRedraw()
- local _ps,_pe,_cp = displayPrint(_p, math.ceil(w/2), 8)
- if not _ps then
- lscroll = true
- return
- end
- clearAndRedraw()
- for c=1,_cp do
- for i=_ps,_pe do
- while _p.getPaperLevel() == 0 do
- displayConfirmDialogue("Page "..i..": Check paper levels", "The paper tray is currently empty. Fill with paper and click here to continue.")
- end
- while _p.getInkLevel() == 0 do
- displayConfirmDialogue("Page "..i..": Check ink levels", "The printer is out of ink. Insert more ink, and click here to continue.")
- end
- --There is no out-tray check... that I can tell at least.
- while not _p:newPage() do
- displayConfirmDialogue("Page "..i..": Check out tray", "The out tray may be full. Remove all pages and click here to continue.")
- end
- _p.setPageTitle((sName or "").." page "..i)
- printPage(doc[i], _p)
- _p:endPage()
- end
- if c<_cp then displayConfirmDialogue("Copy "..c.." Complete!", "Clear out tray and click here to continue") end
- end
- clearAndRedraw()
- displayConfirmDialogue("Print Complete!", "Printed ".._cp.." copies (total "..#doc.." page(s).)")
- end
- lscroll = true
- end
- --[[ A series of functions binded to keyboard shortcuts
- Parameters: none
- Returns: none
- ]]--
- local bindings = {
- --Moves us up. Hitting the top moves the cursor to the start of the line.
- --Most editors do this. I don't know why. Convention?
- [keys.up] = function()
- if currline > 1 then currline = currline - 1
- elseif currpage > 1 then
- currpage = currpage - 1
- currline = #doc[currpage]
- else
- currind = 1
- return
- end
- if currind > #doc[currpage][currline] then
- currind = math.max(1, #doc[currpage][currline] + 1)
- end
- fixCursorPos()
- checkYScroll()
- end,
- --Moves us down. Hitting the bottom moves cursor to end of the line.
- [keys.down] = function()
- if currline < #doc[currpage] then currline = currline + 1
- elseif currpage < #doc then
- currpage = currpage + 1
- currline = 1
- else
- currind = #doc[currpage][currline] + 1
- return
- end
- if currind > #doc[currpage][currline] then
- currind = math.max(1, #doc[currpage][currline] + 1)
- end
- fixCursorPos()
- checkYScroll()
- end,
- --Moves us left. Hitting and edge moves to the end of the last line.
- [keys.left] = function()
- if currind <= 1 then
- if currline > 1 then
- currline = currline - 1
- currind = #doc[currpage][currline] + 1
- elseif currpage > 1 then
- currpage = currpage - 1
- currline = #doc[currpage]
- currind = #doc[currpage][currline] + 1
- end
- else
- currind = currind - 1
- end
- fixCursorPos()
- checkYScroll()
- end,
- --Moves us right. Hitting the edge moves to the start of the next line.
- [keys.right] = function()
- if currind >= #doc[currpage][currline] + 1 or
- doc[currpage][currline]:sub(currind) == "\n" then
- if currline < #doc[currpage] then
- currline = currline + 1
- currind = 1
- elseif currpage < #doc then
- currpage = currpage + 1
- currline = 1
- currind = 1
- end
- else
- currind = currind + 1
- end
- fixCursorPos()
- checkYScroll()
- end,
- --[[ Adds a newline character to the end of the line, and pushes everything down ]]
- [keys.enter] = function()
- --First we put whatever follows the cursor in a carry buffer
- local _cbuff = doc[currpage][currline]:sub(currind)
- doc[currpage][currline] = doc[currpage][currline]:sub(1,currind-1).."\n"
- _desl,_desp = -1,-1
- --We then push the carry buffer down each line in the document
- while nextLine() do
- --This remembers where our cursor needs to be when we're done
- if _desl == -1 or _desp == -1 then
- _desl, _desp = currline, currpage
- end
- --We then swap out our buffer for the current line, to push down.
- local _ctemp = doc[currpage][currline]
- doc[currpage][currline] = _cbuff
- _cbuff = _ctemp
- end
- --We then drop in our last line
- if currline < pagelen then
- doc[currpage][currline+1] = _cbuff
- else
- doc[currpage+1] = { _cbuff }
- end
- --This updates our cursor back to where it should be
- if _desl ~= -1 and _desp ~= -1 then
- currind, currline, currpage = 1, _desl, _desp
- else
- --Called when we hit enter at the end of a document
- nextLine()
- currind = 1
- end
- paginateUnderflow()
- checkYScroll()
- sChange = true
- end,
- --Some more simple cursor manipulation keys
- [keys.home] = function() currind = 1 end,
- [keys["end"]] = function()
- currind = #doc[currpage][currline] + 1
- fixCursorPos()
- end,
- [keys.pageDown] = function()
- if currpage < #doc then
- currpage = currpage + 1
- currline = 1 currind = 1
- checkYScroll()
- end
- end,
- [keys.pageUp] = function()
- if currpage > 1 then
- currpage = currpage - 1
- currline = 1 currind = 1
- checkYScroll()
- end
- end,
- -- Some other core functions in the program
- [keys.backspace] = deleteCharacter,
- [keys.leftCtrl] = performMenu,
- [keys.tab] = function() testMode = not testMode end
- }
- --[[ Performs actions based on keyboard and mouse input
- Parameters: none
- Returns: none
- ]]--
- local function handleInput()
- local _,_cpy = term.getCursorPos()
- if _cpy > 1 then term.setCursorBlink(true) end
- local _id,_p1,_p2,_p3 = os.pullEvent()
- term.setCursorBlink(false)
- if _id == "char" then
- addCharacter(_p1)
- elseif _id == "key" then
- if bindings[_p1] then bindings[_p1]()
- elseif _p1 == keys.leftCtrl then finished = true end
- elseif _id == "mouse_scroll" then
- yscroll = yscroll - _p1
- if yscroll > 4 - _yoff then
- if currpage > 1 then
- yscroll = h - pagelen - _yoff - 3
- currpage = currpage - 1
- currline = 1
- currind = 1
- else yscroll = 4 - _yoff end
- elseif yscroll < h - pagelen - _yoff - 3 then
- if currpage < #doc then
- yscroll = 4 - _yoff
- currpage = currpage + 1
- currline = 1
- currind = 1
- else yscroll = h - pagelen - _yoff - 3 end
- end
- lscroll = true
- elseif _id == "mouse_click" then
- if _p3 < 6 and _p3 == 1 then
- performMenu()
- else
- currline = math.max(_p3 - _yoff - yscroll, 1)
- currline = math.min(currline, #doc[currpage])
- currind = math.max(_p2 - _xoff, 1)
- currind = math.min(currind, #doc[currpage][currline] + 1)
- if currind > 1 and doc[currpage][currline]:sub(currind-1) == "\n"
- then currind = currind - 1 end
- end
- checkYScroll()
- end
- testWait()
- end
- if not term.isColour() then
- print("Currently, WYSIwrite is only supported on advanced computers")
- end
- local _tArgs = {...}
- if _tArgs[1] then
- sName = _tArgs[1]
- sPath = shell.resolve(sName)
- if fs.exists(sPath) then loadDocument(sPath)
- else constructNewDocument(sName, sPath) end
- else constructNewDocument() end
- checkYScroll()
- while not finished do
- --Basic printing stuff
- if lscroll then
- displayClearPage()
- lscroll = false
- end
- term.setBackgroundColour(colours.white)
- term.setTextColour(colours.black)
- printPage(doc[currpage], term, _xoff + 1, _yoff + yscroll)
- displayInterface()
- --Repositioning the cursor
- term.setTextColour(colours.black)
- term.setCursorPos(currind + _xoff, currline + _yoff + yscroll)
- handleInput()
- end
- term.setBackgroundColour(colours.black)
- shell.run("clear")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement