This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Grim Reaper's profile picture

Word Proccesor that prints!

Started by Grim Reaper, 22 September 2012 - 06:01 AM
Grim Reaper #1
Posted 22 September 2012 - 08:01 AM
I just downloaded Computer Craft 1.42 pre-release, and I decided I wanted to write a decent looking word processor that you could print from, and here it is :D/>/>

UPDATE(S):
- Text is now centered when typing.
- The printing of new lines onto paper no longer appears as ?????? followed by what was in the previous lines.
- Margins are drawn to give the user a better feeling for where the end of the line is.
- A bug with the backspace method that allowed infinitely long lines was fixed.
- Tab support added (3 spaces).
- Arrow key movement added.
* Read routine rewritten to fix design flaws that were not allowing me to add certain features.

Here are some screenshots:
SpoilerALBUM: http://imgur.com/a/FV3oC

Code:
SpoilerTrust me, it's much more organized in the pastebin paste. I lose a lot of indentation and white space when I post on the forums, so don't scowl too much when you see the following :P/>/>

-- Word processor written by PaymentOption for ComputerCraft 1.42 --
-- VARIABLES --
bPrinted = false -- Whether or not the document has been printed.
tDocument = {}
bHasPaper = false
nPaperCount = 0
nPaperWidth, nPaperHeight = 0,0
bHasInk = false
nInkCount = 0
bHasPrinter = false
sPrinterSide = nil
bFirstPage = false -- Whether or not the first page has been started.
-- The wrapped printer.
printer = nil
nScreenWidth, nScreenHeight = term.getSize()
nSelection = 1 -- The selection of the print confirmation dialog box.
---------------
-- INITIALIZATION METHODS --
-- Returns true and the side the printer exists on if there a printer is found.
function checkForPrinter()
local tSides = rs.getSides()
-- Run through all of the possible sides and check for a printer peripheral.
for index, side in ipairs( tSides ) do
  -- If a printer is found.
  if peripheral.isPresent( side ) and peripheral.getType( side ) == "printer" then
   return true, side
  end
end

-- No printer was found.
return false, nil
end
-- Returns the wrapped printer object.
function wrapPrinter( side )
return peripheral.wrap( side )
end
-- Returns true and the amount of paper if there is paper; false and nil if there is none.
function checkForPaper( side, printerWrap )
if printerWrap.getPaperLevel() > 0 then
  return true, printerWrap.getPaperLevel()
else
  return false, 0
end
end
-- Returns true and the amount of ink if there is ink; false and nil if there is none.
function checkForInk( side, printerWrap )
if printerWrap.getInkLevel() > 0 then
  return true, printerWrap.getInkLevel()
else
  return false, 0
end
end
-- Returns the width and height of the paper stored.
function getPaperDimensions( printerWrap )
return printerWrap.getPageSize()
end
-- Sets up the page to be printed on: clears it and repositions the cursor to (1, 1)
function setCursorToPrint( printerWrap )
clearPage() -- Clear the page of any previous data.
printerWrap.setCursorPos( 1, 1 )
end
-- Runs all necessary checks, if they pass then true is returned and all variables are setup.
function initializePrinterAndProgram()
local bPrinterExists, sSide = checkForPrinter()

local bPaper, bInk = false
local nPaper, nInk = 0

if bPrinterExists then
  -- Assign the global printer wrap to the newly found and wrapped peripheral.
  printer = wrapPrinter( sSide )
  -- Check for paper and ink amounts.
  bPaper, nPaper = checkForPaper( sSide, printer )
  bInk, nInk = checkForInk( sSide, printer )

  if bPaper and bInk then
   -- Assign the global variables to their proper values.
   bHasPaper = bPaper
   bHasInk = bInk
  
   nPaperCount = nPaper
   nInkCount = nInk
  
   bFirstPage = printer.newPage() -- Start the first page.
   nPaperWidth, nPaperHeight = getPaperDimensions( printer )
   setCursorToPrint( printer ) -- Setup the page so it is blank and the cursor is in the upper left hand corner (1, 1).
   -- Report a successful printer initialization.
   return true
  -- There was either no paper, or no ink.
  else
   -- There was no paper.
   if not bPaper then
	return false, true -- bHasPaper = false, bHasInk = true
   -- There was no ink, but there was paper.
   else
	return true, false -- bHasPaper = true, bHasInk = false
   end
  end
-- No printer was found.
else
  return false
end
end
----------------------------
-- CUSTOM PRINTER METHODS --
-- Clears the current page of all textual data.
function clearPage()
for nCurrentHeight=1, nPaperHeight do
  printer.setCursorPos( 1, nCurrentHeight )
  printer.write( string.rep( " ", nPaperWidth ) )
end
end
-- Prints the page with the specified title and string.
function printPage( sTitle, tPage )
printer.setPageTitle( sTitle )

for nHeight=1, #tPage do
  printer.setCursorPos( 1, nHeight )
  printer.write( tPage[nHeight] )
end

if printer.endPage() then
  return true
else
  return false
end
end
----------------------------
-- TERMINAL ROUTINES (COMPUTER SPECIFIC METHODS) --
function clearScreen()
term.clear()
term.setCursorPos( 1, 1 )
end
-- Prints a string to the very center of the screen on the specified line.
function printCentered( nHeight, sString )
term.setCursorPos( nScreenWidth/2 - string.len( sString )/2, nHeight )
write( sString )
end
-- Prints a string to the very right of the screen on the specified line.
function printRight( nHeight, sString )
term.setCursorPos( nScreenWidth - string.len( sString ), nHeight )
write( sString )
end
-- Draws a box with a width of nBoxWidth.
function drawBox( nBoxWidth )
printCentered( 4, "+" .. string.rep( "-", nBoxWidth ) .. "+" )
for nHeight = 5, 12 do
  printCentered( nHeight, "|" .. string.rep( " ", nBoxWidth ) .. "|" )
end
printCentered( 13, "+" .. string.rep( "-", nBoxWidth ) .. "+" )
end
-- Handles the key presses thrown when we're getting print confirmation.
function handleKeyPress( nKey )
-- If the right key is pressed and the current selection is 'YES'.
if nKey == 205 and nSelection == 1 then
  nSelection = 2
-- If the left key is pressed and the current selection is 'NO'.
elseif nKey == 203 and nSelection == 2 then
  nSelection = 1
-- If the enter key is pressed.
elseif nKey == 28 then
  -- If the user wants to print then print the prompt the user for a tile to the document.
  if nSelection == 1 then
   local sPageTitle = getPageTitle()
   if printPage( sPageTitle, tDocument ) then
	-- End the program.
	clearScreen()
	print( os.version() )
	bPrinted = true
   else
	clearScreen()
	print( os.version() )
	error( "Printing error!" )
   end
  -- If the user does not want to print the re-edit the document.
  elseif nSelection == 2 then
   tDocument = readLines( tDocument )
   getConfirmation()
  end
end
end
-- Reads a fixed amount of characters without ruining a GUI that may be printed on the same line.
function limitRead( nLength, cReplaceChar )
term.setCursorBlink( true )

nLength = nLength or -1 -- -1 is unlimited
sReturnString = ""

xPos, yPos = term.getCursorPos()

while true do
  event, char = os.pullEvent()

  if nLength ~= -1 and string.len( sReturnString ) >= nLength then term.setCursorBlink( false ); return sReturnString end -- Length check

  if event == "char" then sReturnString = sReturnString .. char
  elseif event == "key" and char == 28 then term.setCursorBlink( false ); return sReturnString -- Enter
  elseif event == "key" and char == 14 then -- Backspace
   term.setCursorPos( xPos, yPos )
   term.write( string.rep( " ", string.len( sReturnString ) ) )
   sReturnString = string.sub( sReturnString, 1, string.len( sReturnString )-1 )
   term.setCursorPos( xPos, yPos )
  
   if not cReplaceChar then term.write( sReturnString )
   else term.write( string.rep( cReplaceChar, string.len( sReturnString ) ) ) end
  end

  term.setCursorPos( xPos, yPos )
  term.write( string.rep( " ", string.len( sReturnString ) ) )
  term.setCursorPos( xPos, yPos )
  if not cReplaceChar then term.write( sReturnString )
  else term.write( string.rep( cReplaceChar, string.len( sReturnString ) ) ) end
end
end
-- Takes a line and checks if a word has not finished. If it hasn't
-- then it returns the table of lines with the corrected new line.
--[[function fixTruncation( tLines )
-- Remove the n chracter.
sLine = sLine:sub( 1, string.len( sLine ) - 1 )
-- Check if the line has not finished.
if tLines[#tLines]:sub( 1, 2 ) ~= " " then
  for  in string.gfind(  )
  tLines[#tLines - 1] = tLines[#tLines - 1]:sub( 1,  )
end
end--]]
-- TODO: Remove the truncation effect when jumping lines.
-- Reads multiple lines as one string.
function readLines( tLines )
term.setCursorBlink( true )

local bExistingDocument = nil
if tLines then
  bExistingDocument = true
else
  bExistingDocument = true
end

local tLines = tLines or {}
local nLineWidthCap = nPaperWidth
local nLineCap = nScreenHeight

-- Initialize the lines.
if #tLines == 0 then
  for nLine = 1, nLineCap do
   tLines[nLine] = ""
  end
end

local nReturnKey = 207 -- 'End'

local nCursorPosX = 1
local nCurrentLine = 1
local nCurrentLineLength = 0

-- Draws the paper border so people know where the page ends.
local function drawBorder()
  for nHeight = 1, nScreenHeight do
   term.setCursorPos( 12, nHeight )
   term.write( "|" )
  
   term.setCursorPos( 12 + nPaperWidth + 1, nHeight )
   term.write( "|" )
  end

  term.setCursorPos( 1, nScreenHeight )
  term.write( "'END' menu" )
  printRight( nScreenHeight, "Ln " .. nCurrentLine .. " of ".. nScreenHeight )
end

clearScreen()
drawBorder()
if bExistingDocument then
  for nHeight=1, #tLines do
   term.setCursorPos( 13, nHeight )
   term.write( tLines[nHeight] )
  end
end
while nCurrentLine < nLineCap do
  term.setCursorPos( 13 + nCursorPosX - 1, nCurrentLine )
  local sEvent, cChar = os.pullEvent()

  if sEvent == "char" then
   if tLines[nCurrentLine]:len() + 1 < nLineWidthCap then
	tLines[nCurrentLine] = tLines[nCurrentLine]:sub( 1, nCursorPosX - 1 ) .. cChar .. tLines[nCurrentLine]:sub( nCursorPosX, tLines[nCurrentLine]:len() )
	nCursorPosX = nCursorPosX + 1
   else
	if nCurrentLine + 1 <= nLineCap then
	 tLines[nCurrentLine] = tLines[nCurrentLine] .. cChar
	 nCurrentLine = nCurrentLine+1
	 nCursorPosX = 1
	else
	 term.setCursorBlink( false )
	 return tLines
	end
   end
  elseif sEvent == "key" then
   if cChar == 14 then
	if nCursorPosX - 1 > 0 then
	 tLines[nCurrentLine] = tLines[nCurrentLine]:sub( 1, nCursorPosX - 2 ) .. tLines[nCurrentLine]:sub( nCursorPosX, tLines[nCurrentLine]:len() )
	 nCursorPosX = nCursorPosX - 1
	else
	 if nCurrentLine > 1 then
	  nCurrentLine = nCurrentLine - 1
	  if tLines[nCurrentLine]:len() == nLineWidthCap then
	   tLines[nCurrentLine] = tLines[nCurrentLine]:sub( 1, tLines[nCurrentLine]:len() - 1 )
	  end
	  nCursorPosX = tLines[nCurrentLine]:len() + 1
	 end
	end
   elseif cChar == 28 then
	if nCurrentLine + 1 <= nLineCap then
	 nCurrentLine = nCurrentLine+1
	 nCursorPosX = 1
	else
	 term.setCursorBlink( false )
	 return tLines
	end
   elseif cChar == 203 then
	if nCursorPosX - 1 > 1 then
	 nCursorPosX = nCursorPosX - 1
	end
   elseif cChar == 205 then
	if nCursorPosX + 1 < tLines[nCurrentLine]:len() + 2 then
	 nCursorPosX = nCursorPosX + 1
	end
   elseif cChar == 200 then
	if nCurrentLine - 1 > 0 then
	 nCurrentLine = nCurrentLine - 1
	 if tLines[nCurrentLine]:len() < nCursorPosX then
	  if tLines[nCurrentLine]:len() > 0 then
	   nCursorPosX = tLines[nCurrentLine]:len()
	  else
	   nCursorPosX = 1
	  end
	 end
	end
   elseif cChar == 208 then
	if nCurrentLine + 1 < #tLines then
	 nCurrentLine = nCurrentLine + 1
	 if tLines[nCurrentLine]:len() < nCursorPosX then
	  if tLines[nCurrentLine]:len() > 0 then
	   nCursorPosX = tLines[nCurrentLine]:len()
	  else
	   nCursorPosX = 1
	  end
	 end
	end
   elseif cChar == 15 then
	if tLines[nCurrentLine]:len() + 3 > nLineWidthCap then
	 local nSpaceLeft = nLineWidthCap - tLines[nCurrentLine]:len()
	 if nSpaceLeft > 0 then
	  tLines[nCurrentLine] = tLines[nCurrentLine] .. string.rep( " ", nSpaceLeft )
	 end
	
	 nCurrentLine = nCurrentLine + 1
	 nCursorPosX = 1
	else
	 tLines[nCurrentLine] = tLines[nCurrentLine] .. "   "
	 nCursorPosX = nCursorPosX + 3
	end
   elseif cChar == nReturnKey then
	term.setCursorBlink( false )
	return tLines
   end
  end

  clearScreen()
  drawBorder()
  for nHeight=1, #tLines do
   term.setCursorPos( 13, nHeight )
   term.write( tLines[nHeight] )
  end
end

term.setCursorBlink( false )
return tLines
end
---------------------------------------------------
-- SCREENS --
-- This method gets whether or not the user wants to print the document typed. (used in drawAndGetPrintConfirmationScreen)
function getConfirmation()
local function drawSelections()
  if nSelection == 1 then
   printCentered( 9, "[YES] NO " )
  elseif nSelection == 2 then
   printCentered( 9, " YES [NO]" )
  end
end

-- Draw the confirmation options.
drawBox( 20 )
printCentered( 6, "PRINT?" )

-- Get a 'YES' or 'NO' from the user with a simple menu.
while not bPrinted do
  drawSelections()
  local sEvent, nKey = os.pullEvent( "key" )

  if nKey == 28 or nKey == 205 or nKey == 203 then
   handleKeyPress( nKey )
  end
end

return true
end
-- Gets the title for the page to be printed.
function getPageTitle()
drawBox( 20 )
printCentered( 6, "Page Title:" )
term.setCursorPos( nScreenWidth/2 - 9, 9 )

local sPageTitle = tostring( limitRead( 18 ) )
if not sPageTitle or string.len( sPageTitle ) == 0 then
  sPageTitle = "Default_PageTitle"
end

return sPageTitle
end

-- This screen will prompt the user for a yes or no answer.
function drawAndGetPrintConfirmationScreen()
getConfirmation()
end
------------
initializePrinterAndProgram()
tDocument = readLines()
drawAndGetPrintConfirmationScreen()

Pastebin link: http://pastebin.com/8GVsfU7b

I hope you like it! ;)/>/>

- PaymentOption
Kadecamz #2
Posted 12 October 2012 - 01:23 AM
nice.
basdxz #3
Posted 19 October 2012 - 12:07 AM
A big thanks for this, I was really looking for something to write books with :P/>/>.
anonimo182 #4
Posted 27 October 2012 - 03:38 PM
Hey, can I use this in my OS?
Grim Reaper #5
Posted 05 November 2012 - 05:19 AM
Hey, can I use this in my OS?

Yes. Please, just give credit where it is due.
anonimo182 #6
Posted 05 November 2012 - 05:25 AM
Hey, can I use this in my OS?

Yes. Please, just give credit where it is due.
Thanks, all the credit will be yours :D/>/>