program["Editor"] = function ()
	local color = {
		comment = colors.green,
		text = colors.white,
		key = colors.cyan,
		number = colors.orange,
		string = colors.red,
		operator = colors.blue,
		func = colors.purple,
		icoText = colors.white,
		icoBack = colors.black
	}
	
	if fs.exists("programs/Editor/colors") then
		shell.run("programs/Editor/colors")
		color = editColor
	end
	
	local colorNumbers = {
		["0"] = colors.white,
		["1"] = colors.orange,
		["2"] = colors.magenta,
		["3"] = colors.lightBlue,
		["4"] = colors.yellow,
		["5"] = colors.lime,
		["6"] = colors.pink,
		["7"] = colors.gray,
		["8"] = colors.lightGray,
		["9"] = colors.cyan,
		["a"] = colors.purple,
		["b"] = colors.blue,
		["c"] = colors.brown,
		["d"] = colors.green,
		["e"] = colors.red,
		["f"] = colors.black,
		[" "] = userColor.back
	}
	
	local iconColor, iconTextColor, iconText
	
	local function getColorCode(c)
		if c == colors.white then
			return "0"
		elseif c == colors.orange then
			return "1"
		elseif c == colors.magenta then
			return "2"
		elseif c == colors.lightBlue then
			return "3"
		elseif c == colors.yellow then
			return "4"
		elseif c == colors.lime then
			return "5"
		elseif c == colors.pink then
			return "6"
		elseif c == colors.gray then
			return "7"
		elseif c == colors.lightGray then
			return "8"
		elseif c == colors.cyan then
			return "9"
		elseif c == colors.purple then
			return "a"
		elseif c == colors.blue then
			return "b"
		elseif c == colors.brown then
			return "c"
		elseif c == colors.green then
			return "d"
		elseif c == colors.red then
			return "e"
		elseif c == colors.black then
			return "f"
		end
		
		return " "
	end
	
	function edit()
		-- Get file to edit
		local tArgs = {"programs/Editor/prog"}

		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 = window.getSize()
		local scrollX, scrollY = 0, 0

		local tLines = {}
		local bRunning = true

		-- Colors
		local highlightColor, keywordColor, commentColor, textColor, bgColor, stringColor, functionColor, numberColor, operatorColor

		if window.isColor() then
			bgColor = colors.black
			textColor = color.text
			highlightColor = color.key
			keywordColor = color.key
			commentColor = color.comment
			stringColor = color.string
			functionColor = color.func
			numberColor = color.number
			operatorColor = color.operator
		else
			bgColor = colors.black
			textColor = colors.white
			highlightColor = colors.white
			keywordColor = colors.white
			commentColor = colors.white
			stringColor = colors.white
			functionColor = colors.white
			numberColor = colors.white
			operatorColor = colors.white
		end

		-- Menus
		local bMenu = false
		local nMenuItem = 1
		local tMenuItems

		if bReadOnly then
			tMenuItems = {"Exit"}
		else
			tMenuItems = {"Save", "Exit"}
		end
			
		local sStatus = "Press f1 to access menu"

		local function load(_sPath)
			tLines = {}
			
			if fs.exists(_sPath) then
				local file = io.open(_sPath, "r")
				local sLine = file:read()
				
				while sLine do
					table.insert(tLines, sLine)
					sLine = file:read()
				end
				
				file:close()
			end
			
			if #tLines == 0 then
				table.insert(tLines, "")
			end
		end

		local function save(_sPath)
			-- Create intervening folder
			local sDir = sPath:sub(1, sPath:len() - fs.getName(sPath):len())

			-- Save
			local file = nil
			
			local function innerSave()
				file = fs.open(_sPath, "w")
				
				if file then
					for n, sLine in ipairs(tLines) 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

		local tKeywords = {
			--Keywords
			["and"] = keywordColor,
			["break"] = keywordColor,
			["do"] = keywordColor,
			["else"] = keywordColor,
			["elseif"] = keywordColor,
			["end"] = keywordColor,
			["false"] = keywordColor,
			["for"] = keywordColor,
			["function"] = keywordColor,
			["if"] = keywordColor,
			["in"] = keywordColor,
			["local"] = keywordColor,
			["nil"] = keywordColor,
			["not"] = keywordColor,
			["or"] = keywordColor,
			["repeat"] = keywordColor,
			["return"] = keywordColor,
			["then"] = keywordColor,
			["true"] = keywordColor,
			["until"]= keywordColor,
			["while"] = keywordColor,
			
			--Bit
			["bit"] = functionColor,
			["bit.blshift"] = functionColor,
			["bit.brshift"] = functionColor,
			["bit.blogic_rshift"] = functionColor,
			["bit.bxor"] = functionColor,
			["bit.bor"] = functionColor,
			["bit.band"] = functionColor,
			["bit.bnot"] = functionColor,
			
			--Colors
			["colors.white"] = colors.white,
			["colors.orange"] = colors.orange,
			["colors.magenta"] = colors.magenta,
			["colors.lightBlue"] = colors.lightBlue,
			["colors.yellow"] = colors.yellow,
			["colors.lime"] = colors.lime,
			["colors.pink"] = colors.pink,
			["colors.gray"] = colors.gray,
			["colors.lightGray"] = colors.lightGray,
			["colors.cyan"] = colors.cyan,
			["colors.purple"] = colors.purple,
			["colors.blue"] = colors.blue,
			["colors.brown"] = colors.brown,
			["colors.green"] = colors.green,
			["colors.red"] = colors.red,
			["colors.black"] = colors.black,
			["colors"] = functionColor,
			["colors.combine"] = functionColor,
			["colors.subtract"] = functionColor,
			["colors.test"] = functionColor,
			
			--userColor
			["userColor"] = functionColor,
			["userColor.window"] = userColor.window,
			["userColor.windowText"] = userColor.windowText,
			["userColor.windowDark"] = userColor.windowDark,
			["userColor.text"] = userColor.text,
			["userColor.back"] = userColor.back,
			["userColor.noFocus"] = userColor.noFocus,
			["userColor.desk"] = userColor.desk,
			
			--Commands
			["commands"] = functionColor,
			["commands.exec"] = functionColor,
			["commands.execAsync"] = functionColor,
			["commands.list"] = functionColor,
			["commands.getBlockPosition"] = functionColor,
			["commands.getBlockInfo"] = functionColor,
			["commands.getBlockInfos"] = functionColor,
			
			--Coroutine
			["coroutine"] = functionColor,
			["coroutine.create"] = functionColor,
			["coroutine.resume"] = functionColor,
			["coroutine.running"] = functionColor,
			["coroutine.status"] = functionColor,
			["coroutine.wrap"] = functionColor,
			["coroutine.yield"] = functionColor,
			
			--disk
			["disk"] = functionColor,
			["disk.isPresent"] = functionColor,
			["disk.hasData"] = functionColor,
			["disk.getMountPath"] = functionColor,
			["disk.setLabel"] = functionColor,
			["disk.getLabel"] = functionColor,
			["disk.getId"] = functionColor,
			["disk.hasAudio"] = functionColor,
			["disk.getAudioTitle"] = functionColor,
			["disk.playAudio"] = functionColor,
			["disk.stopAudio"] = functionColor,
			["disk.eject"] = functionColor,
			
			--fs
			["fs"] = functionColor,
			["fs.list"] = functionColor,
			["fs.exists"] = functionColor,
			["fs.isDir"] = functionColor,
			["fs.isReadOnly"] = functionColor,
			["fs.getName"] = functionColor,
			["fs.getDrive"] = functionColor,
			["fs.getSize"] = functionColor,
			["fs.getFreeSpace"] = functionColor,
			["fs.makeDir"] = functionColor,
			["fs.move"] = functionColor,
			["fs.copy"] = functionColor,
			["fs.delete"] = functionColor,
			["fs.combine"] = functionColor,
			["fs.open"] = functionColor,
			["fs.find"] = functionColor,
			["fs.getDir"] = functionColor,
			["fs.complete"] = functionColor,
			
			--gps
			["gps"] = functionColor,
			["gps.locate"] = functionColor,
			
			--help
			["help"] = functionColor,
			["help.path"] = functionColor,
			["help.setPath"] = functionColor,
			["help.lookup"] = functionColor,
			["help.topics"] = functionColor,
			["help.completeTopic"] = functionColor,
			
			--HTTP
			["http"] = functionColor,
			["http.request"] = functionColor,
			["http.get"] = functionColor,
			["http.post"] = functionColor,
			["http.checkURL"] = functionColor,
			
			--io
			["io"] = functionColor,
			["io.open"] = functionColor,
			["io.write"] = functionColor,
			["io.read"] = functionColor,
			
			--Keys
			["keys"] = functionColor,
			["keys.getName"] = functionColor,
			
			--Math
			["math"] = functionColor,
			["math.abs"] = functionColor,
			["math.acos"] = functionColor,
			["math.asin"] = functionColor,
			["math.atan"] = functionColor,
			["math.atan2"] = functionColor,
			["math.ceil"] = functionColor,
			["math.cos"] = functionColor,
			["math.cosh"] = functionColor,
			["math.deg"] = functionColor,
			["math.exp"] = functionColor,
			["math.floor"] = functionColor,
			["math.fmod"] = functionColor,
			["math.frexp"] = functionColor,
			["math.huge"] = functionColor,
			["math.ldexp"] = functionColor,
			["math.log"] = functionColor,
			["math.log10"] = functionColor,
			["math.max"] = functionColor,
			["math.min"] = functionColor,
			["math.modf"] = functionColor,
			["math.pi"] = functionColor,
			["math.pow"] = functionColor,
			["math.rad"] = functionColor,
			["math.random"] = functionColor,
			["math.randomseed"] = functionColor,
			["math.sin"] = functionColor,
			["math.sinh"] = functionColor,
			["math.sqrt"] = functionColor,
			["math.tan"] = functionColor,
			["math.tanh"] = functionColor,
			
			--multishell
			["multishell"] = functionColor,
			["multishell.getCurrent"] = functionColor,
			["multishell.getCount"] = functionColor,
			["multishell.launch"] = functionColor,
			["multishell.setFocus"] = functionColor,
			["multishell.setTitle"] = functionColor,
			["multishell.getTitle"] = functionColor,
			["multishell.getFocus"] = functionColor,
			
			--os
			["os"] = functionColor,
			["os.version"] = functionColor,
			["os.getComputerID"] = functionColor,
			["os.getComputerLabel"] = functionColor,
			["os.run"] = functionColor,
			["os.loadAPI"] = functionColor,
			["os.unloadAPI"] = functionColor,
			["os.pullEvent"] = functionColor,
			["os.pullEventRaw"] = functionColor,
			["os.queueEvent"] = functionColor,
			["os.clock"] = functionColor,
			["os.startTimer"] = functionColor,
			["os.cancelTimer"] = functionColor,
			["os.time"] = functionColor,
			["os.sleep"] = functionColor,
			["sleep"] = functionColor,
			["os.day"] = functionColor,
			["os.setAlarm"] = functionColor,
			["os.cancelAlarm"] = functionColor,
			["os.shutdown"] = functionColor,
			["os.reboot"] = functionColor,
			
			--paintutils
			["paintutils"] = functionColor,
			["paintutils.loadImage"] = functionColor,
			["paintutils.drawImage"] = functionColor,
			["paintutils.drawPixel"] = functionColor,
			["paintutils.drawLine"] = functionColor,
			["paintutils.drawBox"] = functionColor,
			["paintutils.drawFilledBox"] = functionColor,
			
			--parallel
			["parallel"] = functionColor,
			["parallel.waitForAny"] = functionColor,
			["parallel.waitForAll"] = functionColor,
			
			--peripheral
			["peripheral"] = functionColor,
			["peripheral.isPresent"] = functionColor,
			["peripheral.getType"] = functionColor,
			["peripheral.getMethods"] = functionColor,
			["peripheral.call"] = functionColor,
			["peripheral.wrap"] = functionColor,
			["peripheral.find"] = functionColor,
			["peripheral.getNames"] = functionColor,
			
			--rednet
			["rednet"] = functionColor,
			["rednet.open"] = functionColor,
			["rednet.close"] = functionColor,
			["rednet.send"] = functionColor,
			["rednet.broadcast"] = functionColor,
			["rednet.receive"] = functionColor,
			["rednet.isOpen"] = functionColor,
			["rednet.host"] = functionColor,
			["rednet.unhost"] = functionColor,
			["rednet.lookup"] = functionColor,
			["rednet.run"] = functionColor,
			
			--redstone
			["redstone"] = functionColor,
			["redstone.getSides"] = functionColor,
			["redstone.getInput"] = functionColor,
			["redstone.setOutput"] = functionColor,
			["redstone.getOutput"] = functionColor,
			["redstone.getAnalogIutput"] = functionColor,
			["redstone.setAnalogOutput"] = functionColor,
			["redstone.getAnalogOutput"] = functionColor,
			["redstone.getBundledOutput"] = functionColor,
			["redstone.setBundledOutput"] = functionColor,
			["redstone.getBundledIutput"] = functionColor,
			["redstone.testBundledIutput"] = functionColor,
			
			--settings
			["settings"] = functionColor,
			["settings.set"] = functionColor,
			["settings.get"] = functionColor,
			["settings.unset"] = functionColor,
			["settings.clear"] = functionColor,
			["settings.getNames"] = functionColor,
			["settings.load"] = functionColor,
			["settings.save"] = functionColor,
			
			--shell
			["shell"] = functionColor,
			["shell.exit"] = functionColor,
			["shell.dir"] = functionColor,
			["shell.setDir"] = functionColor,
			["shell.path"] = functionColor,
			["shell.setPath"] = functionColor,
			["shell.resolve"] = functionColor,
			["shell.resolveProgram"] = functionColor,
			["shell.aliases"] = functionColor,
			["shell.setAlias"] = functionColor,
			["shell.clearAlias"] = functionColor,
			["shell.programs"] = functionColor,
			["shell.getRunningProgram"] = functionColor,
			["shell.openTab"] = functionColor,
			["shell.switchTab"] = functionColor,
			["shell.complete"] = functionColor,
			["shell.completeProgram"] = functionColor,
			["shell.setCompletionFunction"] = functionColor,
			["shell.getCompletionInfo"] = functionColor,
			
			--string
			["string"] = functionColor,
			["string.byte"] = functionColor,
			["string.char"] = functionColor,
			["string.dump"] = functionColor,
			["string.find"] = functionColor,
			["string.format"] = functionColor,
			["string.gmatch"] = functionColor,
			["string.gsub"] = functionColor,
			["string.len"] = functionColor,
			["string.lower"] = functionColor,
			["string.upper"] = functionColor,
			["string.match"] = functionColor,
			["string.rep"] = functionColor,
			["string.reverse"] = functionColor,
			["string.sub"] = functionColor,
			
			--table
			["table"] = functionColor,
			["table.concat"] = functionColor,
			["table.insert"] = functionColor,
			["table.maxn"] = functionColor,
			["table.remove"] = functionColor,
			["table.sort"] = functionColor,
			
			--window
			["window"] = functionColor,
			["window.write"] = functionColor,
			["window.blit"] = functionColor,
			["window.clear"] = functionColor,
			["window.clearLine"] = functionColor,
			["window.getCursorPos"] = functionColor,
			["window.setCursorPos"] = functionColor,
			["window.setCursorBlink"] = functionColor,
			["window.isColor"] = functionColor,
			["window.getSize"] = functionColor,
			["window.scroll"] = functionColor,
			["window.redirect"] = functionColor,
			["window.current"] = functionColor,
			["window.native"] = functionColor,
			["window.setTextColor"] = functionColor,
			["window.getTextColor"] = functionColor,
			["window.setBackgroundColor"] = functionColor,
			["window.getBackgroundColor"] = functionColor,
			
			--textutils
			["textutils"] = functionColor,
			["textutils.slowWrite"] = functionColor,
			["textutils.slowPrint"] = functionColor,
			["textutils.formatTime"] = functionColor,
			["textutils.tabulate"] = functionColor,
			["textutils.pagedTabulate"] = functionColor,
			["textutils.pagedPrint"] = functionColor,
			["textutils.serialize"] = functionColor,
			["textutils.unserialize"] = functionColor,
			["textutils.serializeJSON"] = functionColor,
			["textutils.urlEncode"] = functionColor,
			["textutils.complete"] = functionColor,

			--turtle
			["turtle"] = functionColor,
			["turtle.craft"] = functionColor,
			["turtle.forward"] = functionColor,
			["turtle.back"] = functionColor,
			["turtle.up"] = functionColor,
			["turtle.down"] = functionColor,
			["turtle.turnLeft"] = functionColor,
			["turtle.turnRight"] = functionColor,
			["turtle.select"] = functionColor,
			["turtle.getSelectedSlot"] = functionColor,
			["turtle.getItemCount"] = functionColor,
			["turtle.getItemSpace"] = functionColor,
			["turtle.getItemDetail"] = functionColor,
			["turtle.equipLeft"] = functionColor,
			["turtle.equipRight"] = functionColor,
			["turtle.attack"] = functionColor,
			["turtle.attackUp"] = functionColor,
			["turtle.attackDown"] = functionColor,
			["turtle.dig"] = functionColor,
			["turtle.digUp"] = functionColor,
			["turtle.digDown"] = functionColor,
			["turtle.place"] = functionColor,
			["turtle.placeUp"] = functionColor,
			["turtle.placeDown"] = functionColor,
			["turtle.detect"] = functionColor,
			["turtle.detectUp"] = functionColor,
			["turtle.detectDown"] = functionColor,
			["turtle.inspect"] = functionColor,
			["turtle.inspectUp"] = functionColor,
			["turtle.inspectDown"] = functionColor,
			["turtle.compare"] = functionColor,
			["turtle.compareUp"] = functionColor,
			["turtle.compareDown"] = functionColor,
			["turtle.compareTo"] = functionColor,
			["turtle.drop"] = functionColor,
			["turtle.dropUp"] = functionColor,
			["turtle.dropDown"] = functionColor,
			["turtle.suck"] = functionColor,
			["turtle.suckUp"] = functionColor,
			["turtle.suckDown"] = functionColor,
			["turtle.refuel"] = functionColor,
			["turtle.getFuelLevel"] = functionColor,
			["turtle.getFuelLimit"] = functionColor,
			["turtle.transferTo"] = functionColor,
			
			--vector
			["vector"] = functionColor,
			["vector.new"] = functionColor,
			
			--lua
			["tonumber"] = functionColor,
			["tostring"] = functionColor,
			["print"] = functionColor,
			["pairs"] = functionColor,
			["ipairs"] = functionColor,
			["pcall"] = functionColor,
			["error"] = functionColor,
		}

		local function tryWrite(sLine, regex, color)
			local match = string.match(sLine, regex)
			
			if match then
				if type(color) == "number" then
					window.setTextColor(color)
				else
					window.setTextColor(color(match))
				end
				
				window.write(match)
				window.setTextColor(textColor)
				return string.sub(sLine, string.len(match) + 1)
			end
			
			return nil
		end

		local function writeHighlighted(sLine)
			function match(m)
				if tKeywords[m] then
					return tKeywords[m]
				end
				return textColor
			end

			while string.len(sLine) > 0 do	
				sLine = 
					tryWrite(sLine, "^%-%-%[%[.-%]%]", commentColor) or
					tryWrite(sLine, "^%-%-.*", commentColor) or
					tryWrite(sLine, "^\".-[^\\]\"", stringColor) or
					tryWrite(sLine, "^\'.-[^\\]\'", stringColor) or
					tryWrite(sLine, "^\"\"", stringColor) or
					tryWrite(sLine, "^''", stringColor) or
					tryWrite(sLine, "^%[%[.-%]%]", stringColor) or
					tryWrite(sLine, "^[.]+", operatorColor) or
					tryWrite(sLine, "^[%d%.]+", numberColor) or
					tryWrite(sLine, "^[%d%a%._]+", match) or
					tryWrite(sLine, "^[^%w_.]", operatorColor)
			end
		end

		local function redrawText()
			for y = 1,h - 1 do
				window.setCursorPos(1 - scrollX, y)
				window.clearLine()

				local sLine = tLines[y + scrollY]
				
				if sLine ~= nil then
					writeHighlighted(sLine)
				end
			end
			
			window.setCursorPos(x - scrollX, y - scrollY)
		end

		local function redrawLine(_nY)
			local sLine = tLines[_nY]
			window.setCursorPos(1 - scrollX, _nY - scrollY)
			window.clearLine()
			writeHighlighted(sLine)
			window.setCursorPos(x - scrollX, _nY - scrollY)
		end

		local function redrawMenu()
			-- Clear line
			window.setCursorPos(1, h)
			window.clearLine()

			-- Draw line numbers
			window.setCursorPos(w - string.len("Ln " .. y) + 1, h)
			window.setTextColor(highlightColor)
			window.write("Ln ")
			window.setTextColor(textColor)
			window.write(y)

			window.setCursorPos(1, h)
			
			if bMenu then
				-- Draw menu
				window.setTextColor(textColor)
				
				for nItem, sItem in pairs(tMenuItems) do
					if nItem == nMenuItem then
						window.setTextColor(highlightColor)
						window.write("[")
						window.setTextColor(textColor)
						window.write(sItem)
						window.setTextColor(highlightColor)
						window.write("]")
						window.setTextColor(textColor)
					else
						window.write(" " .. sItem .. " ")
					end
				end
			else
				-- Draw status
				window.setTextColor(highlightColor)
				window.write(sStatus)
				window.setTextColor(textColor)
			end

			-- Reset cursor
			window.setCursorPos(x - scrollX, y - scrollY)
		end

		local tMenuFuncs = { 
			Save = function ()
				if bReadOnly then
					sStatus = "Access denied"
				else
					local ok, err = save(sPath)
					
					if ok then
						sStatus="Press f1 to access menu"
					else
						sStatus="Press f1 to access menu"
					end
				end
				
				redrawMenu()
			end,
			Print = function ()
				local printer = peripheral.find("printer")
				
				if not printer then
					sStatus = "No printer attached"
					return
				end

				local nPage = 0
				local sName = fs.getName(sPath)
				
				if printer.getInkLevel() < 1 then
					sStatus = "Printer out of ink"
					return
				elseif printer.getPaperLevel() < 1 then
					sStatus = "Printer out of paper"
					return
				end

				local screenTerminal = window.current()
				local printerTerminal = {
					getCursorPos = printer.getCursorPos,
					setCursorPos = printer.setCursorPos,
					getSize = printer.getPageSize,
					write = printer.write,
				}
				printerTerminal.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
			
						window.redirect(screenTerminal)
						redrawMenu()
						window.redirect(printerTerminal)
						
						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
				window.redirect(printerTerminal)
				
				local ok, error = pcall(function ()
					window.scroll()
					for n, sLine in ipairs(tLines) do
						print(sLine)
					end
				end)
				
				window.redirect(screenTerminal)
				
				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,
			Exit = function ()
				bRunning = false
			end
		}

		local function doMenuItem(_n)
			tMenuFuncs[tMenuItems[_n]]()
			
			if bMenu then
				bMenu = false
				window.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
			window.setCursorPos(screenX, screenY)
			
			-- Statusbar now pertains to menu, it would probably be safe to redraw the menu on every key event.
			redrawMenu()
		end

		-- Actual program functionality begins
		load(sPath)

		window.setBackgroundColor(bgColor)
		window.clear()
		window.setCursorPos(x, y)
		window.setCursorBlink(true)

		redrawText()
		redrawMenu()

		-- Handle input
		while bRunning do
			local sEvent, param, param2, param3 = os.pullEvent()
			window.restoreCursor()
			
			if sEvent == "key" then
				if param == keys.up then
					-- Up
					if not bMenu then
						if y > 1 then
							-- Move cursor up
							y = y - 1
							x = math.min(x, string.len(tLines[y]) + 1)
							setCursor(x, y)
						end
					end
				elseif param == keys.down then
					-- Down
					if not bMenu then
						-- Move cursor down
						if y < #tLines then
							y = y + 1
							x = math.min(x, string.len(tLines[y]) + 1)
							setCursor(x, y)
						end
					end
				elseif param == keys.tab then
					-- Tab
					if not bMenu and not bReadOnly then
						-- Indent line
						tLines[y] = "  " .. tLines[y]
						x = x + 2
						setCursor(x, y)
						redrawLine(y)
					end
				elseif param == keys.pageUp then
					-- Page Up
					if not bMenu then
						-- Move up a page
						if y - (h - 1) >= 1 then
							y = y - (h - 1)
						else
							y = 1
						end
						x = math.min(x, string.len(tLines[y]) + 1)
						setCursor(x, y)
					end
				elseif param == keys.pageDown then
					-- Page Down
					if not bMenu then
						-- Move down a page
						if y + (h - 1) <= #tLines then
							y = y + (h - 1)
						else
							y = #tLines
						end
						x = math.min(x, string.len(tLines[y]) + 1)
						setCursor(x, y)
					end
				elseif param == keys.home then
					-- Home
					if not bMenu then
						-- Move cursor to the beginning
						x = 1
						setCursor(x, y)
					end
				elseif param == keys["end"] then
					-- End
					if not bMenu then
						-- Move cursor to the end
						x = string.len(tLines[y]) + 1
						setCursor(x, y)
					end
				elseif param == keys.left then
					-- Left
					if not bMenu then
						if x > 1 then
							-- Move cursor left
							x = x - 1
						elseif x == 1 and y > 1 then
							x = string.len(tLines[y - 1]) + 1
							y = y - 1
						end
						setCursor(x, y)
					else
						-- Move menu left
						nMenuItem = nMenuItem - 1
						if nMenuItem < 1 then
							nMenuItem = #tMenuItems
						end
						redrawMenu()
					end
				elseif param == keys.right then
					-- Right
					if not bMenu then
						if x < string.len(tLines[y]) + 1 then
							-- Move cursor right
							x = x + 1
						elseif x == string.len(tLines[y]) + 1 and y < #tLines then
							x = 1
							y = y + 1
						end
						setCursor(x, y)
					else
						-- Move menu right
						nMenuItem = nMenuItem + 1
						if nMenuItem > #tMenuItems then
							nMenuItem = 1
						end
						redrawMenu()
					end
				elseif param == keys.delete then
					-- Delete
					if not bMenu and not bReadOnly then
						if  x < string.len(tLines[y]) + 1 then
							local sLine = tLines[y]
							tLines[y] = string.sub(sLine, 1, x - 1) .. string.sub(sLine, x + 1)
							redrawLine(y)
						elseif y < #tLines then
							tLines[y] = tLines[y] .. tLines[y + 1]
							table.remove(tLines, y + 1)
							redrawText()
							redrawMenu()
						end
					end
				elseif param == keys.backspace then
					-- Backspace
					if not bMenu and not bReadOnly then
						if x > 1 then
							-- Remove character
							local sLine = tLines[y]
							tLines[y] = string.sub(sLine, 1, x - 2) .. string.sub(sLine, x)
							redrawLine(y)
					
							x = x - 1
							setCursor(x, y)
						elseif y > 1 then
							-- Remove newline
							local sPrevLen = string.len(tLines[y - 1])
							tLines[y - 1] = tLines[y - 1] .. tLines[y]
							table.remove(tLines, y)
							redrawText()
						
							x = sPrevLen + 1
							y = y - 1
							setCursor(x, y)
						end
					end
				elseif param == keys.enter then
					-- Enter
					if not bMenu and not bReadOnly then
						-- Newline
						local sLine = tLines[y]
						local _, spaces = string.find(sLine, "^[ ]+")
						
						if not spaces then
							spaces = 0
						end
						
						tLines[y] = string.sub(sLine, 1, x - 1)
						table.insert(tLines, y + 1, string.rep(' ', spaces) .. string.sub(sLine, x))
						redrawText()
					
						x = spaces + 1
						y = y + 1
						setCursor(x, y)
					elseif bMenu then
						-- Menu selection
						doMenuItem(nMenuItem)
					end
				elseif param == keys.f1 then
					-- Menu toggle
					bMenu = not bMenu
					
					if bMenu then
						window.setCursorBlink(false)
					else
						window.setCursorBlink(true)
					end
					
					redrawMenu()
				end
				
			elseif sEvent == "char" then
				if not bMenu and not bReadOnly then
					-- Input text
					local sLine = tLines[y]
					tLines[y] = string.sub(sLine, 1, x - 1) .. param .. string.sub(sLine, x)
					redrawLine(y)
				
					x = x + 1
					setCursor(x, y)
				elseif bMenu then
					-- Select menu items
					for n, sMenuItem in ipairs(tMenuItems) do
						if string.lower(string.sub(sMenuItem, 1, 1)) == string.lower(param) then
							doMenuItem(n)
							break
						end
					end
				end
			elseif sEvent == "paste" then
				if not bMenu and not bReadOnly then
					-- Input text
					local sLine = tLines[y]
					tLines[y] = string.sub(sLine, 1, x - 1) .. param .. string.sub(sLine, x)
					redrawLine(y)

					x = x + string.len(param)
					setCursor(x, y)
				end
			elseif sEvent == "mouse_click" then
				if not bMenu then
					if param == 1 then
						-- Left click
						local cx, cy = param2, param3 - 1
						if cy < h then
							y = math.min(math.max(scrollY + cy, 1), #tLines)
							x = math.min(math.max(scrollX + cx, 1), string.len(tLines[y]) + 1)
							setCursor(x, y)
						end
					end
				end
			elseif sEvent == "mouse_scroll" then
				if not bMenu then
					if param == -1 then
						-- Scroll up
						if scrollY > 0 then
							-- Move cursor up
							scrollY = scrollY - 1
							redrawText()
						end
					elseif param == 1 then
						-- Scroll down
						local nMaxScroll = #tLines - (h - 1)
						
						if scrollY < nMaxScroll then
							-- Move cursor down
							scrollY = scrollY + 1
							redrawText()
						end
					end
				end

			elseif sEvent == "window_resize" then
				w, h = window.getSize()
				setCursor(x, y)
				redrawMenu()
				redrawText()
			end
		end

		-- Cleanup
		window.clear()
		window.setCursorBlink(false)
		window.setCursorPos(1, 1)
	end
	
	local function drawText(text, x, y, fColor, bColor)
		window.setTextColor(fColor)
		window.setBackgroundColor(bColor)
		window.setCursorPos(x, y)
		window.write(text)
	end
	
	local current = ""
	local w, h = window.getSize()
	local ix, iy
	local l
	
	local function display(b)
	
		local backColor = userColor.back
	
		if b == false then
			backColor = userColor.noFocus
		end
	
		window.setBackgroundColor(backColor)
		window.clear()
		
		--Preview
		drawText("Preview:", 2, 2, userColor.text, backColor)
		
		drawText("--Comment              ", 11, 2, color.comment, colors.black)
		
		drawText("if ", 11, 3, color.key, colors.black)
		window.setTextColor(color.text)
		window.write("number ")
		window.setTextColor(color.operator)
		window.write("== ")
		window.setTextColor(color.number)
		window.write("3 ")
		window.setTextColor(color.key)
		window.write("then    ")
		
		drawText("  window.write", 11, 4, color.func, colors.black)
		window.setTextColor(color.operator)
		window.write("(")
		window.setTextColor(color.string)
		window.write("\"Hello\"")
		window.setTextColor(color.operator)
		window.write(")")
		
		drawText("end                    ", 11, 5, color.key, colors.black)
		
		--Colors
		drawText("Click on a color to change it", 2, 7, userColor.text, backColor)
		drawText("Text color:", 2, 8, userColor.text, backColor)
		drawText("  ", 15, 8, userColor.text, color.text)
		drawText("Comment:", 2, 9, userColor.text, backColor)
		drawText("  ", 15, 9, userColor.text, color.comment)
		drawText("Keywords:", 2, 10, userColor.text, backColor)
		drawText("  ", 15, 10, userColor.text, color.key)
		drawText("Number:", 2, 11, userColor.text, backColor)
		drawText("  ", 15, 11, userColor.text, color.number)
		drawText("String:", 2, 12, userColor.text, backColor)
		drawText("  ", 15, 12, userColor.text, color.string)
		drawText("Operator:", 2, 13, userColor.text, backColor)
		drawText("  ", 15, 13, userColor.text, color.operator)
		drawText("Function:", 2, 14, userColor.text, backColor)
		drawText("  ", 15, 14, userColor.text, color.func)
		
		drawText("edit program", 2, 16, userColor.windowText, userColor.windowDark)
		drawText("save on disk", 18, 16, userColor.windowText, userColor.windowDark)
		drawText("test", 34, 16, userColor.windowText, userColor.windowDark)
		
		--Icon
		drawText("Icon: ", w - 14, 2, userColor.text, backColor)
		drawText("Click on the icon", w - 16, 7, userColor.text, backColor)
		drawText("to change color", w - 16, 8, userColor.text, backColor)
		drawText("and add text", w - 16, 9, userColor.text, backColor)
		
		drawText("Icon Text:", w - 16, 10, userColor.text, backColor)
		
		if color.icoText == "none" then
			drawText("--", w - 5, 10, userColor.text, userColor.back)
		else
			drawText("  ", w - 5, 10, userColor.text, color.icoText)
		end
		
		drawText("Icon Back:", w - 16, 11, userColor.text, backColor)
		
		if color.icoBack == "none" then
			drawText("--", w - 5, 11, userColor.text, userColor.back)
		else
			drawText("  ", w - 5, 11, userColor.text, color.icoBack)
		end
		
		local icoFile = io.open("programs/Editor/progIcon", "r")
		iconColor, iconTextColor, iconText = icoFile:read(), icoFile:read(), icoFile:read()
		icoFile:close()
		
		local x, y = w - 10, 2
		
		for i = 1,9 do
			local dx = i
			local dy = 0
			
			while dx > 3 do
				dx = dx - 3
				dy = dy + 1
			end
			
			window.setCursorPos(dx + x + 1, dy + y)
			window.setBackgroundColor(colorNumbers[iconColor:sub(i,i)])
			window.setTextColor(colorNumbers[iconTextColor:sub(i,i)])
			window.write(iconText:sub(i,i))
		end
		
		if current ~= "" then
			drawText(" ", 22, 9, colors.white, colors.white)
			drawText(" ", 23, 9, colors.white, colors.orange)
			drawText(" ", 24, 9, colors.white, colors.magenta)
			drawText(" ", 25, 9, colors.white, colors.lightBlue)
			
			drawText(" ", 22, 10, colors.white, colors.yellow)
			drawText(" ", 23, 10, colors.white, colors.lime)
			drawText(" ", 24, 10, colors.white, colors.pink)
			drawText(" ", 25, 10, colors.white, colors.gray)
			
			drawText(" ", 22, 11, colors.white, colors.lightGray)
			drawText(" ", 23, 11, colors.white, colors.cyan)
			drawText(" ", 24, 11, colors.white, colors.purple)
			drawText(" ", 25, 11, colors.white, colors.blue)
			
			drawText(" ", 22, 12, colors.white, colors.brown)
			drawText(" ", 23, 12, colors.white, colors.green)
			drawText(" ", 24, 12, colors.white, colors.red)
			drawText(" ", 25, 12, colors.white, colors.black)
			
			if current == "icoBack" or current == "icoText" then
				drawText("None", 22, 13, userColor.windowText, userColor.windowDark)
			end
		end
	end
	
	while true do
		display()
		
		local event, b, x, y = os.pullEvent()
		
		if event == "mouse_click" then
		
			y = y - 1
			
			if x == 15 or x == 16 then
				if y == 8 then
					current = "text"
				elseif y == 9 then
					current = "comment"
				elseif y == 10 then
					current = "key"
				elseif y == 11 then
					current = "number"
				elseif y == 12 then
					current = "string"
				elseif y == 13 then
					current = "operator"
				elseif y == 14 then
					current = "func"
				end
			elseif x == w - 5 or x == w - 4 then
				if y == 10 then
					current = "icoText"
				elseif y == 11 then
					current = "icoBack"
				end
			elseif current ~= "" then
				if x == 22 and y == 9 then
					color[current] = colors.white
				elseif x == 23 and y == 9 then
					color[current] = colors.orange
				elseif x == 24 and y == 9 then
					color[current] = colors.magenta
				elseif x == 25 and y == 9 then
					color[current] = colors.lightBlue
				elseif x == 22 and y == 10 then
					color[current] = colors.yellow
				elseif x == 23 and y == 10 then
					color[current] = colors.lime
				elseif x == 24 and y == 10 then
					color[current] = colors.pink
				elseif x == 25 and y == 10 then
					color[current] = colors.gray
				elseif x == 22 and y == 11 then
					color[current] = colors.lightGray
				elseif x == 23 and y == 11 then
					color[current] = colors.cyan
				elseif x == 24 and y == 11 then
					color[current] = colors.purple
				elseif x == 25 and y == 11 then
					color[current] = colors.blue
				elseif x == 22 and y == 12 then
					color[current] = colors.brown
				elseif x == 23 and y == 12 then
					color[current] = colors.green
				elseif x == 24 and y == 12 then
					color[current] = colors.red
				elseif x == 25 and y == 12 then
					color[current] = colors.black
				elseif (current == "icoText" or current == "icoBack") and x >= 22 and x <= 25 and y == 13 then
					color[current] = "none"
				end
				
				local file = io.open("programs/Editor/colors", "w")
				file:write("editColor = {\n")
				
				for k, v in pairs(color) do
					file:write(k .. " = " .. v .. ",\n")
				end
				
				file:write("}")
				file:close()
			
				current = ""
			elseif x >= 2 and x <= 13 and y == 16 then
				edit()
			elseif x >= 18 and x <= 29 and y == 16 then
				display(false)
				
				--Save on Disk
				while true do
					window.setBackgroundColor(userColor.windowDark)
					window.setTextColor(userColor.windowText)
					window.setCursorPos(w / 2 - 6, h / 2)
					window.write("Save")
					window.setBackgroundColor(userColor.window)
					window.write("         ")
					window.setBackgroundColor(colors.red)
					window.setTextColor(colors.white)
					window.write("X")
				
					if disk.hasData("left") or disk.hasData("right") or disk.hasData("back") or disk.hasData("front") or disk.hasData("top") or disk.hasData("bottom") then
						
						window.setBackgroundColor(userColor.back)
						window.setTextColor(userColor.text)
						window.setCursorPos(w / 2 - 6, h / 2 + 1)
						window.write("Write the name")
						window.setCursorPos(w / 2 - 6, h / 2 + 2)
						window.write("of the program")
						window.setCursorPos(w / 2 - 6, h / 2 + 3)
						window.write(string.rep(" ", 14))
						
						local event, key, p2, p3
						local name = {}
						local br
						
						window.setCursorBlink(true)
						window.setCursorPos(w / 2 - 6, h / 2 + 3)
						
						while key ~= keys.enter or event ~= "key" do
							event, key, p2, p3 = os.pullEvent()
							window.restoreCursor()
							
							if event == "char" then
								name[#name + 1] = key
							elseif event == "key" and key == keys.backspace then
								name[#name] = nil
							elseif event == "mouse_click" and p2 == 32 and p3 == 9 then
								br = true
								window.setCursorBlink(false)
								break
							end
							
							window.setCursorPos(w / 2 - 6, h / 2 + 3)
							window.write(string.rep(" ", 14))
							window.setCursorPos(w / 2 - 6, h / 2 + 3)
							
							local str = table.concat(name)
							
							if #str > 13 then
								str = str:sub(#str - 12, #str)
							end
							
							window.write(str)
						end
						
						if br then
							break
						end
						
						window.setCursorBlink(false)
						
						window.setCursorPos(w / 2 - 6, h / 2 + 1)
						window.write(string.rep(" ", 14))
						window.setCursorPos(w / 2 - 6, h / 2 + 2)
						window.write("Program Saved ")
						window.setCursorPos(w / 2 - 6, h / 2 + 3)
						window.write(string.rep(" ", 14))
						
						if fs.exists("disk/prog") then
							fs.delete("disk/prog")
						end
						
						if fs.exists("disk/icon") then
							fs.delete("disk/icon")
						end
						
						fs.copy("programs/Editor/prog", "disk/prog")
						fs.copy("programs/Editor/progIcon", "disk/icon")
						
						local file = fs.open("disk/prog", "r")
						local code = file.readAll()
						file:close()
						
						file = fs.open("disk/prog", "w")
						file.write("program[\"" .. table.concat(name):gsub(" ", "_") .. "\"] = function ()\n" .. code .. "\nend")
						file:close()
						
						sleep(2)
						break
					end
					
					window.setBackgroundColor(userColor.back)
					window.setTextColor(userColor.text)
					window.setCursorPos(w / 2 - 6, h / 2 + 1)
					window.write(string.rep(" ", 14))
					
					window.setBackgroundColor(userColor.back)
					window.setTextColor(userColor.text)
					window.setCursorPos(w / 2 - 6, h / 2 + 2)
					window.write(" Insert Disk  ")
					
					window.setBackgroundColor(userColor.back)
					window.setTextColor(userColor.text)
					window.setCursorPos(w / 2 - 6, h / 2 + 3)
					window.write(string.rep(" ", 14))
					
					local ev, param1, param2, param3 = os.pullEvent()
					
					if ev == "mouse_click" and param2 == 32 and param3 == 9 then
						window.setCursorBlink(false)
						break
					end
				end
				
			elseif x >= 34 and x < 38 and y == 16 then
				pcall(function ()
					shell.run("programs/Editor/prog")
				end)
				sleep(5)
			elseif x >= w - 8 and x <= w - 6 and y >= 2 and y <= 4 then
				ix, iy = x - w + 8, y - 2
				
				l = ix + 1 + iy * 3
				
				local file = io.open("programs/Editor/progIcon", "w")
				
				if l > 1 and l < 9 then
					file:write(iconColor:sub(1, l - 1) .. getColorCode(color.icoBack) .. iconColor:sub(l + 1, 9) .. "\n")
					file:write(iconTextColor:sub(1, l - 1) .. getColorCode(color.icoText) .. iconTextColor:sub(l + 1, 9) .. "\n")
					file:write(iconText:sub(1, l - 1) .. " " .. iconText:sub(l + 1, 9))
				elseif l == 1 then
					file:write(getColorCode(color.icoBack) .. iconColor:sub(2, 9) .. "\n")
					file:write(getColorCode(color.icoText) .. iconTextColor:sub(2, 9) .. "\n")
					file:write(" " .. iconText:sub(2, 9))
				else
					file:write(iconColor:sub(1, 8) .. getColorCode(color.icoBack) .. "\n")
					file:write(iconTextColor:sub(1, 8) .. getColorCode(color.icoText) .. "\n")
					file:write(iconText:sub(1, 8) .. " ")
				end
				
				file:close()
			end
			
		elseif event == "char" and ix then
			local file = io.open("programs/Editor/progIcon", "w")
				
			if l > 1 and l < 9 then
				file:write(iconColor:sub(1, l - 1) .. getColorCode(color.icoBack) .. iconColor:sub(l + 1, 9) .. "\n")
				file:write(iconTextColor:sub(1, l - 1) .. getColorCode(color.icoText) .. iconTextColor:sub(l + 1, 9) .. "\n")
				file:write(iconText:sub(1, l - 1) .. b .. iconText:sub(l + 1, 9))
			elseif l == 1 then
				file:write(getColorCode(color.icoBack) .. iconColor:sub(2, 9) .. "\n")
				file:write(getColorCode(color.icoText) .. iconTextColor:sub(2, 9) .. "\n")
				file:write(b .. iconText:sub(2, 9))
			else
				file:write(iconColor:sub(1, 8) .. getColorCode(color.icoBack) .. "\n")
				file:write(iconTextColor:sub(1, 8) .. getColorCode(color.icoText) .. "\n")
				file:write(iconText:sub(1, 8) .. b)
			end
			
			file:close()
		end
	end
end