From 621e13d8bed7215a076e505eba998c7a0ec38519 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Sun, 15 Nov 2020 13:37:32 -0500 Subject: New program system; Capitalized folders --- Programs/Ink.bup/Contents/Info.meta | 1 + .../Ink.bup/Contents/Resources/resources_here.txt | 1 + Programs/Ink.bup/Contents/bits-UI/ink.lua | 3153 ++++++++++++++++++++ 3 files changed, 3155 insertions(+) create mode 100644 Programs/Ink.bup/Contents/Info.meta create mode 100644 Programs/Ink.bup/Contents/Resources/resources_here.txt create mode 100644 Programs/Ink.bup/Contents/bits-UI/ink.lua (limited to 'Programs/Ink.bup') diff --git a/Programs/Ink.bup/Contents/Info.meta b/Programs/Ink.bup/Contents/Info.meta new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/Programs/Ink.bup/Contents/Info.meta @@ -0,0 +1 @@ + diff --git a/Programs/Ink.bup/Contents/Resources/resources_here.txt b/Programs/Ink.bup/Contents/Resources/resources_here.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/Programs/Ink.bup/Contents/Resources/resources_here.txt @@ -0,0 +1 @@ + diff --git a/Programs/Ink.bup/Contents/bits-UI/ink.lua b/Programs/Ink.bup/Contents/bits-UI/ink.lua new file mode 100644 index 0000000..49f6119 --- /dev/null +++ b/Programs/Ink.bup/Contents/bits-UI/ink.lua @@ -0,0 +1,3153 @@ +tArgs = {...} + +if OneOS then + --running under OneOS + OneOS.ToolBarColour = colours.grey + OneOS.ToolBarTextColour = colours.white +end + +local _w, _h = term.getSize() + +local round = function(num, idp) + local mult = 10^(idp or 0) + return math.floor(num * mult + 0.5) / mult +end + +UIColours = { + Toolbar = colours.grey, + ToolbarText = colours.lightGrey, + ToolbarSelected = colours.lightBlue, + ControlText = colours.white, + ToolbarItemTitle = colours.black, + Background = colours.lightGrey, + MenuBackground = colours.white, + MenuText = colours.black, + MenuSeparatorText = colours.grey, + MenuDisabledText = colours.lightGrey, + Shadow = colours.grey, + TransparentBackgroundOne = colours.white, + TransparentBackgroundTwo = colours.lightGrey, + MenuBarActive = colours.white +} + +local getNames = peripheral.getNames or function() + local tResults = {} + for n,sSide in ipairs( rs.getSides() ) do + if peripheral.isPresent( sSide ) then + table.insert( tResults, sSide ) + local isWireless = false + if not pcall(function()isWireless = peripheral.call(sSide, 'isWireless') end) then + isWireless = true + end + if peripheral.getType( sSide ) == "modem" and not isWireless then + local tRemote = peripheral.call( sSide, "getNamesRemote" ) + for n,sName in ipairs( tRemote ) do + table.insert( tResults, sName ) + end + end + end + end + return tResults +end + +Peripheral = { + GetPeripheral = function(_type) + for i, p in ipairs(Peripheral.GetPeripherals()) do + if p.Type == _type then + return p + end + end + end, + + Call = function(type, ...) + local tArgs = {...} + local p = Peripheral.GetPeripheral(type) + peripheral.call(p.Side, unpack(tArgs)) + end, + + GetPeripherals = function(filterType) + local peripherals = {} + for i, side in ipairs(getNames()) do + local name = peripheral.getType(side):gsub("^%l", string.upper) + local code = string.upper(side:sub(1,1)) + if side:find('_') then + code = side:sub(side:find('_')+1) + end + + local dupe = false + for i, v in ipairs(peripherals) do + if v[1] == name .. ' ' .. code then + dupe = true + end + end + + if not dupe then + local _type = peripheral.getType(side) + local isWireless = false + if _type == 'modem' then + if not pcall(function()isWireless = peripheral.call(sSide, 'isWireless') end) then + isWireless = true + end + if isWireless then + _type = 'wireless_modem' + name = 'W '..name + end + end + if not filterType or _type == filterType then + table.insert(peripherals, {Name = name:sub(1,8) .. ' '..code, Fullname = name .. ' ('..side:sub(1, 1):upper() .. side:sub(2, -1)..')', Side = side, Type = _type, Wireless = isWireless}) + end + end + end + return peripherals + end, + + PresentNamed = function(name) + return peripheral.isPresent(name) + end, + + CallType = function(type, ...) + local tArgs = {...} + local p = GetPeripheral(type) + return peripheral.call(p.Side, unpack(tArgs)) + end, + + CallNamed = function(name, ...) + local tArgs = {...} + return peripheral.call(name, unpack(tArgs)) + end +} + +TextLine = { + Text = "", + Alignment = AlignmentLeft, + + Initialise = function(self, text, alignment) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Text = text + new.Alignment = alignment or AlignmentLeft + return new + end +} + +local StripColours = function(str) + return str:gsub('['..string.char(14)..'-'..string.char(29)..']','') +end + +Printer = { + Name = nil, + PeripheralType = 'printer', + + paperLevel = function(self) + return Peripheral.CallNamed(self.Name, 'getPaperLevel') + end, + + newPage = function(self) + return Peripheral.CallNamed(self.Name, 'newPage') + end, + + endPage = function(self) + return Peripheral.CallNamed(self.Name, 'endPage') + end, + + pageWrite = function(self, text) + return Peripheral.CallNamed(self.Name, 'write', text) + end, + + setPageTitle = function(self, title) + return Peripheral.CallNamed(self.Name, 'setPageTitle', title) + end, + + inkLevel = function(self) + return Peripheral.CallNamed(self.Name, 'getInkLevel') + end, + + getCursorPos = function(self) + return Peripheral.CallNamed(self.Name, 'getCursorPos') + end, + + setCursorPos = function(self, x, y) + return Peripheral.CallNamed(self.Name, 'setCursorPos', x, y) + end, + + pageSize = function(self) + return Peripheral.CallNamed(self.Name, 'getPageSize') + end, + + Present = function() + if Peripheral.GetPeripheral(Printer.PeripheralType) == nil then + return false + else + return true + end + end, + + PrintLines = function(self, lines, title, copies) + local pages = {} + local pageLines = {} + for i, line in ipairs(lines) do + table.insert(pageLines, TextLine:Initialise(StripColours(line))) + if i % 25 == 0 then + table.insert(pages, pageLines) + pageLines = {} + end + end + if #pageLines ~= 0 then + table.insert(pages, pageLines) + end + return self:PrintPages(pages, title, copies) + end, + + PrintPages = function(self, pages, title, copies) + copies = copies or 1 + for c = 1, copies do + for p, page in ipairs(pages) do + if self:paperLevel() < #pages * copies then + return 'Add more paper to the printer' + end + if self:inkLevel() < #pages * copies then + return 'Add more ink to the printer' + end + self:newPage() + for i, line in ipairs(page) do + self:setCursorPos(1, i) + self:pageWrite(StripColours(line.Text)) + end + if title then + self:setPageTitle(title) + end + self:endPage() + end + end + end, + + Initialise = function(self, name) + if Printer.Present() then --fix + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + if name and Peripheral.PresentNamed(name) then + new.Name = name + else + new.Name = Peripheral.GetPeripheral(Printer.PeripheralType).Side + end + return new + end + end +} + +Clipboard = { + Content = nil, + Type = nil, + IsCut = false, + + Empty = function() + Clipboard.Content = nil + Clipboard.Type = nil + Clipboard.IsCut = false + end, + + isEmpty = function() + return Clipboard.Content == nil + end, + + Copy = function(content, _type) + Clipboard.Content = content + Clipboard.Type = _type or 'generic' + Clipboard.IsCut = false + end, + + Cut = function(content, _type) + Clipboard.Content = content + Clipboard.Type = _type or 'generic' + Clipboard.IsCut = true + end, + + Paste = function() + local c, t = Clipboard.Content, Clipboard.Type + if Clipboard.IsCut then + Clipboard.Empty() + end + return c, t + end +} + +if OneOS and OneOS.Clipboard then + Clipboard = OneOS.Clipboard +end + +Drawing = { + + Screen = { + Width = _w, + Height = _h + }, + + DrawCharacters = function (x, y, characters, textColour,bgColour) + Drawing.WriteStringToBuffer(x, y, characters, textColour, bgColour) + end, + + DrawBlankArea = function (x, y, w, h, colour) + Drawing.DrawArea (x, y, w, h, " ", 1, colour) + end, + + DrawArea = function (x, y, w, h, character, textColour, bgColour) + --width must be greater than 1, other wise we get a stack overflow + if w < 0 then + w = w * -1 + elseif w == 0 then + w = 1 + end + + for ix = 1, w do + local currX = x + ix - 1 + for iy = 1, h do + local currY = y + iy - 1 + Drawing.WriteToBuffer(currX, currY, character, textColour, bgColour) + end + end + end, + + DrawImage = function(_x,_y,tImage, w, h) + if tImage then + for y = 1, h do + if not tImage[y] then + break + end + for x = 1, w do + if not tImage[y][x] then + break + end + local bgColour = tImage[y][x] + local textColour = tImage.textcol[y][x] or colours.white + local char = tImage.text[y][x] + Drawing.WriteToBuffer(x+_x-1, y+_y-1, char, textColour, bgColour) + end + end + elseif w and h then + Drawing.DrawBlankArea(x, y, w, h, colours.green) + end + end, + --using .nft + LoadImage = function(path) + local image = { + text = {}, + textcol = {} + } + local fs = fs + if OneOS then + fs = OneOS.FS + end + if fs.exists(path) then + local _open = io.open + if OneOS then + _open = OneOS.IO.open + end + local file = _open(path, "r") + local sLine = file:read() + local num = 1 + while sLine do + table.insert(image, num, {}) + table.insert(image.text, num, {}) + table.insert(image.textcol, num, {}) + + --As we're no longer 1-1, we keep track of what index to write to + local writeIndex = 1 + --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour + local bgNext, fgNext = false, false + --The current background and foreground colours + local currBG, currFG = nil,nil + for i=1,#sLine do + local nextChar = string.sub(sLine, i, i) + if nextChar:byte() == 30 then + bgNext = true + elseif nextChar:byte() == 31 then + fgNext = true + elseif bgNext then + currBG = Drawing.GetColour(nextChar) + bgNext = false + elseif fgNext then + currFG = Drawing.GetColour(nextChar) + fgNext = false + else + if nextChar ~= " " and currFG == nil then + currFG = colours.white + end + image[num][writeIndex] = currBG + image.textcol[num][writeIndex] = currFG + image.text[num][writeIndex] = nextChar + writeIndex = writeIndex + 1 + end + end + num = num+1 + sLine = file:read() + end + file:close() + end + return image + end, + + DrawCharactersCenter = function(x, y, w, h, characters, textColour,bgColour) + w = w or Drawing.Screen.Width + h = h or Drawing.Screen.Height + x = x or 0 + y = y or 0 + x = math.ceil((w - #characters) / 2) + x + y = math.floor(h / 2) + y + + Drawing.DrawCharacters(x, y, characters, textColour, bgColour) + end, + + GetColour = function(hex) + if hex == ' ' then + return colours.transparent + end + local value = tonumber(hex, 16) + if not value then return nil end + value = math.pow(2,value) + return value + end, + + Clear = function (_colour) + _colour = _colour or colours.black + Drawing.ClearBuffer() + Drawing.DrawBlankArea(1, 1, Drawing.Screen.Width, Drawing.Screen.Height, _colour) + end, + + Buffer = {}, + BackBuffer = {}, + + DrawBuffer = function() + for y,row in pairs(Drawing.Buffer) do + for x,pixel in pairs(row) do + local shouldDraw = true + local hasBackBuffer = true + if Drawing.BackBuffer[y] == nil or Drawing.BackBuffer[y][x] == nil or #Drawing.BackBuffer[y][x] ~= 3 then + hasBackBuffer = false + end + if hasBackBuffer and Drawing.BackBuffer[y][x][1] == Drawing.Buffer[y][x][1] and Drawing.BackBuffer[y][x][2] == Drawing.Buffer[y][x][2] and Drawing.BackBuffer[y][x][3] == Drawing.Buffer[y][x][3] then + shouldDraw = false + end + if shouldDraw then + term.setBackgroundColour(pixel[3]) + term.setTextColour(pixel[2]) + term.setCursorPos(x, y) + term.write(pixel[1]) + end + end + end + Drawing.BackBuffer = Drawing.Buffer + Drawing.Buffer = {} + term.setCursorPos(1,1) + end, + + ClearBuffer = function() + Drawing.Buffer = {} + end, + + WriteStringToBuffer = function (x, y, characters, textColour,bgColour) + for i = 1, #characters do + local character = characters:sub(i,i) + Drawing.WriteToBuffer(x + i - 1, y, character, textColour, bgColour) + end + end, + + WriteToBuffer = function(x, y, character, textColour,bgColour) + x = round(x) + y = round(y) + if bgColour == colours.transparent then + Drawing.Buffer[y] = Drawing.Buffer[y] or {} + Drawing.Buffer[y][x] = Drawing.Buffer[y][x] or {"", colours.white, colours.black} + Drawing.Buffer[y][x][1] = character + Drawing.Buffer[y][x][2] = textColour + else + Drawing.Buffer[y] = Drawing.Buffer[y] or {} + Drawing.Buffer[y][x] = {character, textColour, bgColour} + end + end, +} + +Current = { + Document = nil, + TextInput = nil, + CursorPos = {1,1}, + CursorColour = colours.black, + Selection = {8, 36}, + Window = nil, + Modified = false, +} + +local isQuitting = false + +function OrderSelection() + if Current.Selection then + if Current.Selection[1] <= Current.Selection[2] then + return Current.Selection + else + return {Current.Selection[2], Current.Selection[1]} + end + end +end + +function StripColours(str) + return str:gsub('['..string.char(14)..'-'..string.char(29)..']','') +end + +function FindColours(str) + local _, count = str:gsub('['..string.char(14)..'-'..string.char(29)..']','') + return count +end + +ColourFromCharacter = function(character) + local n = character:byte() - 14 + if n > 16 then + return nil + else + return 2^n + end +end + +CharacterFromColour = function(colour) + return string.char(math.floor(math.log(colour)/math.log(2))+14) +end + +Events = {} + +Button = { + X = 1, + Y = 1, + Width = 0, + Height = 0, + BackgroundColour = colours.lightGrey, + TextColour = colours.white, + ActiveBackgroundColour = colours.lightGrey, + Text = "", + Parent = nil, + _Click = nil, + Toggle = nil, + + AbsolutePosition = function(self) + return self.Parent:AbsolutePosition() + end, + + Draw = function(self) + local bg = self.BackgroundColour + local tc = self.TextColour + if type(bg) == 'function' then + bg = bg() + end + + if self.Toggle then + tc = UIColours.MenuBarActive + bg = self.ActiveBackgroundColour + end + + local pos = GetAbsolutePosition(self) + Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, bg) + Drawing.DrawCharactersCenter(pos.X, pos.Y, self.Width, self.Height, self.Text, tc, bg) + end, + + Initialise = function(self, x, y, width, height, backgroundColour, parent, click, text, textColour, toggle, activeBackgroundColour) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + height = height or 1 + new.Width = width or #text + 2 + new.Height = height + new.Y = y + new.X = x + new.Text = text or "" + new.BackgroundColour = backgroundColour or colours.lightGrey + new.TextColour = textColour or colours.white + new.ActiveBackgroundColour = activeBackgroundColour or colours.lightGrey + new.Parent = parent + new._Click = click + new.Toggle = toggle + return new + end, + + Click = function(self, side, x, y) + if self._Click then + if self:_Click(side, x, y, not self.Toggle) ~= false and self.Toggle ~= nil then + self.Toggle = not self.Toggle + Draw() + end + return true + else + return false + end + end +} + +TextBox = { + X = 1, + Y = 1, + Width = 0, + Height = 0, + BackgroundColour = colours.lightGrey, + TextColour = colours.black, + Parent = nil, + TextInput = nil, + Placeholder = '', + + AbsolutePosition = function(self) + return self.Parent:AbsolutePosition() + end, + + Draw = function(self) + local pos = GetAbsolutePosition(self) + Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, self.BackgroundColour) + local text = self.TextInput.Value + if #tostring(text) > (self.Width - 2) then + text = text:sub(#text-(self.Width - 3)) + if Current.TextInput == self.TextInput then + Current.CursorPos = {pos.X + 1 + self.Width-2, pos.Y} + end + else + if Current.TextInput == self.TextInput then + Current.CursorPos = {pos.X + 1 + self.TextInput.CursorPos, pos.Y} + end + end + + if #tostring(text) == 0 then + Drawing.DrawCharacters(pos.X + 1, pos.Y, self.Placeholder, colours.lightGrey, self.BackgroundColour) + else + Drawing.DrawCharacters(pos.X + 1, pos.Y, text, self.TextColour, self.BackgroundColour) + end + + term.setCursorBlink(true) + + Current.CursorColour = self.TextColour + end, + + Initialise = function(self, x, y, width, height, parent, text, backgroundColour, textColour, done, numerical) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + height = height or 1 + new.Width = width or #text + 2 + new.Height = height + new.Y = y + new.X = x + new.TextInput = TextInput:Initialise(text or '', function(key) + if done then + done(key) + end + Draw() + end, numerical) + new.BackgroundColour = backgroundColour or colours.lightGrey + new.TextColour = textColour or colours.black + new.Parent = parent + return new + end, + + Click = function(self, side, x, y) + Current.Input = self.TextInput + self:Draw() + end +} + +TextInput = { + Value = "", + Change = nil, + CursorPos = nil, + Numerical = false, + IsDocument = nil, + + Initialise = function(self, value, change, numerical, isDocument) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Value = tostring(value) + new.Change = change + new.CursorPos = #tostring(value) + new.Numerical = numerical + new.IsDocument = isDocument or false + return new + end, + + Insert = function(self, str) + if self.Numerical then + str = tostring(tonumber(str)) + end + + local selection = OrderSelection() + + if self.IsDocument and selection then + self.Value = string.sub(self.Value, 1, selection[1]-1) .. str .. string.sub( self.Value, selection[2]+2) + self.CursorPos = selection[1] + Current.Selection = nil + else + local _, newLineAdjust = string.gsub(self.Value:sub(1, self.CursorPos), '\n','') + + self.Value = string.sub(self.Value, 1, self.CursorPos + newLineAdjust) .. str .. string.sub( self.Value, self.CursorPos + 1 + newLineAdjust) + self.CursorPos = self.CursorPos + 1 + end + + self.Change(key) + end, + + Extract = function(self, remove) + local selection = OrderSelection() + if self.IsDocument and selection then + local _, newLineAdjust = string.gsub(self.Value:sub(selection[1], selection[2]), '\n','') + local str = string.sub(self.Value, selection[1], selection[2]+1+newLineAdjust) + if remove then + self.Value = string.sub(self.Value, 1, selection[1]-1) .. string.sub( self.Value, selection[2]+2+newLineAdjust) + self.CursorPos = selection[1] - 1 + Current.Selection = nil + end + return str + end + end, + + Char = function(self, char) + if char == 'nil' then + return + end + self:Insert(char) + end, + + Key = function(self, key) + if key == keys.enter then + if self.IsDocument then + self.Value = string.sub(self.Value, 1, self.CursorPos ) .. '\n' .. string.sub( self.Value, self.CursorPos + 1 ) + self.CursorPos = self.CursorPos + 1 + end + self.Change(key) + elseif key == keys.left then + -- Left + if self.CursorPos > 0 then + local colShift = FindColours(string.sub( self.Value, self.CursorPos, self.CursorPos)) + self.CursorPos = self.CursorPos - 1 - colShift + self.Change(key) + end + + elseif key == keys.right then + -- Right + if self.CursorPos < string.len(self.Value) then + local colShift = FindColours(string.sub( self.Value, self.CursorPos+1, self.CursorPos+1)) + self.CursorPos = self.CursorPos + 1 + colShift + self.Change(key) + end + + elseif key == keys.backspace then + -- Backspace + if self.IsDocument and Current.Selection then + self:Extract(true) + self.Change(key) + elseif self.CursorPos > 0 then + local colShift = FindColours(string.sub( self.Value, self.CursorPos, self.CursorPos)) + local _, newLineAdjust = string.gsub(self.Value:sub(1, self.CursorPos), '\n','') + + self.Value = string.sub( self.Value, 1, self.CursorPos - 1 - colShift + newLineAdjust) .. string.sub( self.Value, self.CursorPos + 1 - colShift + newLineAdjust) + self.CursorPos = self.CursorPos - 1 - colShift + self.Change(key) + end + elseif key == keys.home then + -- Home + self.CursorPos = 0 + self.Change(key) + elseif key == keys.delete then + if self.IsDocument and Current.Selection then + self:Extract(true) + self.Change(key) + elseif self.CursorPos < string.len(self.Value) then + self.Value = string.sub( self.Value, 1, self.CursorPos ) .. string.sub( self.Value, self.CursorPos + 2 ) + self.Change(key) + end + elseif key == keys["end"] then + -- End + self.CursorPos = string.len(self.Value) + self.Change(key) + elseif key == keys.up and self.IsDocument then + -- Up + if Current.Document.CursorPos then + local page = Current.Document.Pages[Current.Document.CursorPos.Page] + self.CursorPos = page:GetCursorPosFromPoint(Current.Document.CursorPos.Collum + page.MarginX, Current.Document.CursorPos.Line - page.MarginY - 1 + Current.Document.ScrollBar.Scroll, true) + self.Change(key) + end + elseif key == keys.down and self.IsDocument then + -- Down + if Current.Document.CursorPos then + local page = Current.Document.Pages[Current.Document.CursorPos.Page] + self.CursorPos = page:GetCursorPosFromPoint(Current.Document.CursorPos.Collum + page.MarginX, Current.Document.CursorPos.Line - page.MarginY + 1 + Current.Document.ScrollBar.Scroll, true) + self.Change(key) + end + end + end +} + +Menu = { + X = 0, + Y = 0, + Width = 0, + Height = 0, + Owner = nil, + Items = {}, + RemoveTop = false, + + Draw = function(self) + Drawing.DrawBlankArea(self.X + 1, self.Y + 1, self.Width, self.Height, UIColours.Shadow) + if not self.RemoveTop then + Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, UIColours.MenuBackground) + for i, item in ipairs(self.Items) do + if item.Separator then + Drawing.DrawArea(self.X, self.Y + i, self.Width, 1, '-', colours.grey, UIColours.MenuBackground) + else + local textColour = item.Colour or UIColours.MenuText + if (item.Enabled and type(item.Enabled) == 'function' and item.Enabled() == false) or item.Enabled == false then + textColour = UIColours.MenuDisabledText + end + Drawing.DrawCharacters(self.X + 1, self.Y + i, item.Title, textColour, UIColours.MenuBackground) + end + end + else + Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, UIColours.MenuBackground) + for i, item in ipairs(self.Items) do + if item.Separator then + Drawing.DrawArea(self.X, self.Y + i - 1, self.Width, 1, '-', colours.grey, UIColours.MenuBackground) + else + local textColour = item.Colour or UIColours.MenuText + if (item.Enabled and type(item.Enabled) == 'function' and item.Enabled() == false) or item.Enabled == false then + textColour = UIColours.MenuDisabledText + end + Drawing.DrawCharacters(self.X + 1, self.Y + i - 1, item.Title, textColour, UIColours.MenuBackground) + + Drawing.DrawCharacters(self.X - 1 + self.Width-#item.KeyName, self.Y + i - 1, item.KeyName, textColour, UIColours.MenuBackground) + end + end + end + end, + + NameForKey = function(self, key) + if key == keys.leftCtrl then + return '^' + elseif key == keys.tab then + return 'Tab' + elseif key == keys.delete then + return 'Delete' + elseif key == keys.n then + return 'N' + elseif key == keys.a then + return 'A' + elseif key == keys.s then + return 'S' + elseif key == keys.o then + return 'O' + elseif key == keys.z then + return 'Z' + elseif key == keys.y then + return 'Y' + elseif key == keys.c then + return 'C' + elseif key == keys.x then + return 'X' + elseif key == keys.v then + return 'V' + elseif key == keys.r then + return 'R' + elseif key == keys.l then + return 'L' + elseif key == keys.t then + return 'T' + elseif key == keys.h then + return 'H' + elseif key == keys.e then + return 'E' + elseif key == keys.p then + return 'P' + elseif key == keys.f then + return 'F' + elseif key == keys.m then + return 'M' + elseif key == keys.q then + return 'Q' + else + return '?' + end + end, + + Initialise = function(self, x, y, items, owner, removeTop) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + if not owner then + return + end + + local keyNames = {} + + for i, v in ipairs(items) do + items[i].KeyName = '' + if v.Keys then + for _i, key in ipairs(v.Keys) do + items[i].KeyName = items[i].KeyName .. self:NameForKey(key) + end + end + if items[i].KeyName ~= '' then + table.insert(keyNames, items[i].KeyName) + end + end + local keysLength = LongestString(keyNames) + if keysLength > 0 then + keysLength = keysLength + 2 + end + + new.Width = LongestString(items, 'Title') + 2 + keysLength + if new.Width < 10 then + new.Width = 10 + end + new.Height = #items + 2 + new.RemoveTop = removeTop or false + if removeTop then + new.Height = new.Height - 1 + end + + if y < 1 then + y = 1 + end + if x < 1 then + x = 1 + end + + if y + new.Height > Drawing.Screen.Height + 1 then + y = Drawing.Screen.Height - new.Height + end + if x + new.Width > Drawing.Screen.Width + 1 then + x = Drawing.Screen.Width - new.Width + end + + + new.Y = y + new.X = x + new.Items = items + new.Owner = owner + return new + end, + + New = function(self, x, y, items, owner, removeTop) + if Current.Menu and Current.Menu.Owner == owner then + Current.Menu = nil + return + end + + local new = self:Initialise(x, y, items, owner, removeTop) + Current.Menu = new + return new + end, + + Click = function(self, side, x, y) + local i = y-1 + if self.RemoveTop then + i = y + end + if i >= 1 and y < self.Height then + if not ((self.Items[i].Enabled and type(self.Items[i].Enabled) == 'function' and self.Items[i].Enabled() == false) or self.Items[i].Enabled == false) and self.Items[i].Click then + self.Items[i]:Click() + if Current.Menu.Owner and Current.Menu.Owner.Toggle then + Current.Menu.Owner.Toggle = false + end + Current.Menu = nil + self = nil + end + return true + end + end +} + +MenuBar = { + X = 1, + Y = 1, + Width = Drawing.Screen.Width, + Height = 1, + MenuBarItems = {}, + + AbsolutePosition = function(self) + return {X = self.X, Y = self.Y} + end, + + Draw = function(self) + --Drawing.DrawArea(self.X - 1, self.Y, 1, self.Height, "|", UIColours.ToolbarText, UIColours.Background) + + Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, colours.grey) + for i, button in ipairs(self.MenuBarItems) do + button:Draw() + end + end, + + Initialise = function(self, items) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.X = 1 + new.Y = 1 + new.MenuBarItems = items + return new + end, + + AddToolbarItem = function(self, item) + table.insert(self.ToolbarItems, item) + self:CalculateToolbarItemPositions() + end, + + CalculateToolbarItemPositions = function(self) + local currY = 1 + for i, toolbarItem in ipairs(self.ToolbarItems) do + toolbarItem.Y = currY + currY = currY + toolbarItem.Height + end + end, + + Click = function(self, side, x, y) + for i, item in ipairs(self.MenuBarItems) do + if item.X <= x and item.X + item.Width > x then + if item:Click(item, side, x - item.X + 1, 1) then + return true + end + end + end + return false + end +} + +TextFormatPlainText = 1 +TextFormatInkText = 2 + +Document = { + X = 1, + Y = 1, + PageSize = {Width = 25, Height = 21}, + TextInput = nil, + Pages = {}, + Format = TextFormatPlainText, + Title = '', + Path = nil, + ScrollBar = nil, + Lines = {}, + CursorPos = nil, + + CalculateLineWrapping = function(self) + local limit = self.PageSize.Width + local text = self.TextInput.Value + local lines = {''} + local words = {} + + for word, space in text:gmatch('(%S+)(%s*)') do + for i = 1, math.ceil(#word/limit) do + local _space = '' + if i == math.ceil(#word/limit) then + _space = space + end + table.insert(words, {word:sub(1+limit*(i-1), limit*i), _space}) + end + end + + for i, ws in ipairs(words) do + local word = ws[1] + local space = ws[2] + local temp = lines[#lines] .. word .. space:gsub('\n','') + if #temp > limit then + table.insert(lines, '') + end + if space:find('\n') then + lines[#lines] = lines[#lines] .. word + + space = space:gsub('\n', function() + table.insert(lines, '') + return '' + end) + else + lines[#lines] = lines[#lines] .. word .. space + end + end + return lines + end, + + CalculateCursorPos = function(self) + local passedCharacters = 0 + Current.CursorPos = nil + for p, page in ipairs(self.Pages) do + page:Draw() + if not Current.CursorPos then + for i, line in ipairs(page.Lines) do + local relCursor = self.TextInput.CursorPos - FindColours(self.TextInput.Value:sub(1,self.TextInput.CursorPos)) + if passedCharacters + #StripColours(line.Text:gsub('\n','')) >= relCursor then + Current.CursorPos = {self.X + page.MarginX + (relCursor - passedCharacters), page.Y + 1 + i} + self.CursorPos = {Page = p, Line = i, Collum = relCursor - passedCharacters - FindColours(self.TextInput.Value:sub(1,self.TextInput.CursorPos-1))} + break + end + passedCharacters = passedCharacters + #StripColours(line.Text:gsub('\n','')) + end + end + end + end, + + Draw = function(self) + self:CalculatePages() + self:CalculateCursorPos() + self.ScrollBar:Draw() + end, + + CalculatePages = function(self) + self.Pages = {} + local lines = self:CalculateLineWrapping() + self.Lines = lines + local pageLines = {} + local totalPageHeight = (3 + self.PageSize.Height + 2 * Page.MarginY) + for i, line in ipairs(lines) do + table.insert(pageLines, TextLine:Initialise(line)) + if i % self.PageSize.Height == 0 then + table.insert(self.Pages, Page:Initialise(self, pageLines, 3 - self.ScrollBar.Scroll + totalPageHeight*(#self.Pages))) + pageLines = {} + end + end + if #pageLines ~= 0 then + table.insert(self.Pages, Page:Initialise(self, pageLines, 3 - self.ScrollBar.Scroll + totalPageHeight*(#self.Pages))) + end + + self.ScrollBar.MaxScroll = totalPageHeight*(#self.Pages) - Drawing.Screen.Height + 1 + end, + + ScrollToCursor = function(self) + self:CalculateCursorPos() + if Current.CursorPos and + (Current.CursorPos[2] > Drawing.Screen.Height + or Current.CursorPos[2] < 2) then + self.ScrollBar:DoScroll(Current.CursorPos[2] - Drawing.Screen.Height) + end + end, + + SetSelectionColour = function(self, colour) + local selection = OrderSelection() + local text = self.TextInput:Extract(true) + local colChar = CharacterFromColour(colour) + local precedingColour = '' + if FindColours(self.TextInput.Value:sub(self.TextInput.CursorPos+1, self.TextInput.CursorPos+1)) == 0 then + for i = 1, self.TextInput.CursorPos do + local c = self.TextInput.Value:sub(self.TextInput.CursorPos - i,self.TextInput.CursorPos - i) + if FindColours(c) == 1 then + precedingColour = c + break + end + end + if precedingColour == '' then + precedingColour = CharacterFromColour(colours.black) + end + end + + self.TextInput:Insert(colChar..StripColours(text)..precedingColour) + --text = text:gsub('['..string.char(14)..'-'..string.char(29)..']','') + end, + + Initialise = function(self, text, title, path) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Title = title or 'New Document' + new.Path = path + new.X = (Drawing.Screen.Width - (new.PageSize.Width + 2*(Page.MarginX)))/2 + new.Y = 2 + new.TextInput = TextInput:Initialise(text, function() + new:ScrollToCursor() + Current.Modified = true + Draw() + end, false, true) + new.ScrollBar = ScrollBar:Initialise(Drawing.Screen.Width, new.Y, Drawing.Screen.Height-1, 0, nil, nil, nil, function()end) + Current.TextInput = new.TextInput + Current.ScrollBar = new.ScrollBar + return new + end +} + +ScrollBar = { + X = 1, + Y = 1, + Width = 1, + Height = 1, + BackgroundColour = colours.grey, + BarColour = colours.lightBlue, + Parent = nil, + Change = nil, + Scroll = 0, + MaxScroll = 0, + ClickPoint = nil, + + AbsolutePosition = function(self) + return self.Parent:AbsolutePosition() + end, + + Draw = function(self) + local pos = GetAbsolutePosition(self) + local barHeight = self.Height - self.MaxScroll + if barHeight < 3 then + barHeight = 3 + end + local percentage = (self.Scroll/self.MaxScroll) + + Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, self.BackgroundColour) + Drawing.DrawBlankArea(pos.X, pos.Y + round(self.Height*percentage - barHeight*percentage), self.Width, barHeight, self.BarColour) + end, + + Initialise = function(self, x, y, height, maxScroll, backgroundColour, barColour, parent, change) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Width = 1 + new.Height = height + new.Y = y + new.X = x + new.BackgroundColour = backgroundColour or colours.grey + new.BarColour = barColour or colours.lightBlue + new.Parent = parent + new.Change = change or function()end + new.MaxScroll = maxScroll + new.Scroll = 0 + return new + end, + + DoScroll = function(self, amount) + amount = round(amount) + if self.Scroll < 0 or self.Scroll > self.MaxScroll then + return false + end + self.Scroll = self.Scroll + amount + if self.Scroll < 0 then + self.Scroll = 0 + elseif self.Scroll > self.MaxScroll then + self.Scroll = self.MaxScroll + end + self.Change() + return true + end, + + Click = function(self, side, x, y, drag) + local percentage = (self.Scroll/self.MaxScroll) + local barHeight = (self.Height - self.MaxScroll) + if barHeight < 3 then + barHeight = 3 + end + local relScroll = (self.MaxScroll*(y + barHeight*percentage)/self.Height) + if not drag then + self.ClickPoint = self.Scroll - relScroll + 1 + end + + if self.Scroll-1 ~= relScroll then + self:DoScroll(relScroll-self.Scroll-1 + self.ClickPoint) + end + return true + end +} + +AlignmentLeft = 1 +AlignmentCentre = 2 +AlignmentRight = 3 + +TextLine = { + Text = "", + Alignment = AlignmentLeft, + + Initialise = function(self, text, alignment) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Text = text + new.Alignment = alignment or AlignmentLeft + return new + end +} + +local clickPos = 1 +Page = { + X = 1, + Y = 1, + Width = 1, + Height = 1, + MarginX = 3, + MarginY = 2, + BackgroundColour = colours.white, + TextColour = colours.white, + ActiveBackgroundColour = colours.lightGrey, + Lines = {}, + Parent = nil, + + AbsolutePosition = function(self) + return self.Parent:AbsolutePosition() + end, + + Draw = function(self) + local pos = GetAbsolutePosition(self) + + if pos.Y > Drawing.Screen.Height or pos.Y + self.Height < 1 then + return + end + + Drawing.DrawBlankArea(pos.X+self.Width,pos.Y -1 + 1, 1, self.Height, UIColours.Shadow) + Drawing.DrawBlankArea(pos.X+1, pos.Y -1 + self.Height, self.Width, 1, UIColours.Shadow) + Drawing.DrawBlankArea(pos.X, pos.Y -1, self.Width, self.Height, self.BackgroundColour) + + local textColour = self.TextColour + if not Current.Selection then + for i, line in ipairs(self.Lines) do + local _c = 1 + for c = 1, #line.Text do + local col = ColourFromCharacter(line.Text:sub(c,c)) + if col then + textColour = col + else + Drawing.WriteToBuffer(pos.X + self.MarginX - 1 + _c, pos.Y -2 + i + self.MarginY, line.Text:sub(c,c), textColour, self.BackgroundColour) + _c = _c + 1 + end + end + end + else + local selection = OrderSelection() + local char = 1 + local textColour = self.TextColour + for i, line in ipairs(self.Lines) do + local _c = 1 + for c = 1, #line.Text do + local col = ColourFromCharacter(line.Text:sub(c,c)) + if col then + textColour = col + else + local tc = textColour + local colour = colours.white + if char >= selection[1] and char <= selection[2] then + colour = colours.lightBlue + tc = colours.white + end + + Drawing.WriteToBuffer(pos.X + self.MarginX - 1 + _c, pos.Y -2 + i + self.MarginY, line.Text:sub(c,c), tc, colour) + _c = _c + 1 + end + char = char + 1 + end + end + end + end, + + Initialise = function(self, parent, lines, y) + local new = {} -- the new instanc + setmetatable( new, {__index = self} ) + new.Height = parent.PageSize.Height + 2 * self.MarginY + new.Width = parent.PageSize.Width + 2 * self.MarginX + new.X = 1 + new.Y = y or 1 + new.Lines = lines or {} + new.BackgroundColour = colours.white + new.TextColour = colours.black + new.Parent = parent + new.ClickPos = 1 + return new + end, + + GetCursorPosFromPoint = function(self, x, y, rel) + local pos = GetAbsolutePosition(self) + if rel then + pos = {Y = 0, X = 0} + end + local row = y - pos.Y + self.MarginY - self.Parent.ScrollBar.Scroll + local col = x - self.MarginX - pos.X + 1 + local cursorPos = 0 + if row <= 0 or col <= 0 then + return 0 + end + + if row > #self.Lines then + for i, v in ipairs(self.Lines) do + cursorPos = cursorPos + #v.Text-- - FindColours(v.Text) + end + return cursorPos + end + + --term.setCursorPos(1,3) + local prevLineCount = 0 + for i, v in ipairs(self.Lines) do + if i == row then + if col > #v.Text then + col = #v.Text-- + FindColours(v.Text) + else + col = col + FindColours(v.Text:sub(1, col)) + end + --term.setCursorPos(1,2) + --print(prevLineCount) + cursorPos = cursorPos + col + 2 - i - prevLineCount + break + else + prevLineCount = FindColours(v.Text) + if prevLineCount ~= 0 then + prevLineCount = prevLineCount + end + cursorPos = cursorPos + #v.Text + 2 - i + FindColours(v.Text) + end + end + + return cursorPos - 2 + end, + + Click = function(self, side, x, y, drag) + local cursorPos = self:GetCursorPosFromPoint(x, y) + self.Parent.TextInput.CursorPos = cursorPos + if drag == nil then + Current.Selection = nil + clickPos = x + else + local relCursor = cursorPos-- - FindColours(self.Parent.TextInput.Value:sub(1,cursorPos)) + 1 + if not Current.Selection then + local adder = 1 + if clickPos and clickPos < x then + adder = 0 + end + Current.Selection = {relCursor + adder, relCursor + 1 + adder} + else + Current.Selection[2] = relCursor + 1 + end + end + Draw() + return true + end +} + +function GetAbsolutePosition(object) + local obj = object + local i = 0 + local x = 1 + local y = 1 + while true do + x = x + obj.X - 1 + y = y + obj.Y - 1 + + if not obj.Parent then + return {X = x, Y = y} + end + + obj = obj.Parent + + if i > 32 then + return {X = 1, Y = 1} + end + + i = i + 1 + end + +end + +function Draw() + if not Current.Window then + Drawing.Clear(colours.lightGrey) + else + Drawing.DrawArea(1, 2, Drawing.Screen.Width, Drawing.Screen.Height, '|', colours.black, colours.lightGrey) + end + + if Current.Document then + Current.Document:Draw() + end + + Current.MenuBar:Draw() + + if Current.Window then + Current.Window:Draw() + end + + if Current.Menu then + Current.Menu:Draw() + end + + Drawing.DrawBuffer() + + if Current.TextInput and Current.CursorPos and not Current.Menu and not(Current.Window and Current.Document and Current.TextInput == Current.Document.TextInput) and Current.CursorPos[2] > 1 then + term.setCursorPos(Current.CursorPos[1], Current.CursorPos[2]) + term.setCursorBlink(true) + term.setTextColour(Current.CursorColour) + else + term.setCursorBlink(false) + end +end +MainDraw = Draw + +LongestString = function(input, key) + local length = 0 + for i = 1, #input do + local value = input[i] + if key then + if value[key] then + value = value[key] + else + value = '' + end + end + local titleLength = string.len(value) + if titleLength > length then + length = titleLength + end + end + return length +end + +function LoadMenuBar() + Current.MenuBar = MenuBar:Initialise({ + Button:Initialise(1, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) + if toggle then + Menu:New(1, 2, { + { + Title = "New...", + Click = function() + Current.Document = Document:Initialise('') + end, + Keys = { + keys.leftCtrl, + keys.n + } + }, + { + Title = 'Open...', + Click = function() + DisplayOpenDocumentWindow() + end, + Keys = { + keys.leftCtrl, + keys.o + } + }, + { + Separator = true + }, + { + Title = 'Save...', + Click = function() + SaveDocument() + end, + Keys = { + keys.leftCtrl, + keys.s + }, + Enabled = function() + return true + end + }, + { + Separator = true + }, + { + Title = 'Print...', + Click = function() + PrintDocument() + end, + Keys = { + keys.leftCtrl, + keys.p + }, + Enabled = function() + return true + end + }, + { + Separator = true + }, + { + Title = 'Quit', + Click = function() + Close() + end + }, + --[[ + { + Title = 'Save As...', + Click = function() + + end + } + ]]-- + }, self, true) + else + Current.Menu = nil + end + return true + end, 'File', colours.lightGrey, false), + Button:Initialise(7, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) + if not self.Toggle then + Menu:New(7, 2, { + --[[ + { + Title = "Undo", + Click = function() + end, + Keys = { + keys.leftCtrl, + keys.z + }, + Enabled = function() + return false + end + }, + { + Title = 'Redo', + Click = function() + + end, + Keys = { + keys.leftCtrl, + keys.y + }, + Enabled = function() + return false + end + }, + { + Separator = true + }, + ]]-- + { + Title = 'Cut', + Click = function() + Clipboard.Cut(Current.Document.TextInput:Extract(true), 'text') + end, + Keys = { + keys.leftCtrl, + keys.x + }, + Enabled = function() + return Current.Document ~= nil and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil + end + }, + { + Title = 'Copy', + Click = function() + Clipboard.Copy(Current.Document.TextInput:Extract(), 'text') + end, + Keys = { + keys.leftCtrl, + keys.c + }, + Enabled = function() + return Current.Document ~= nil and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil + end + }, + { + Title = 'Paste', + Click = function() + local paste = Clipboard.Paste() + Current.Document.TextInput:Insert(paste) + Current.Document.TextInput.CursorPos = Current.Document.TextInput.CursorPos + #paste - 1 + end, + Keys = { + keys.leftCtrl, + keys.v + }, + Enabled = function() + return Current.Document ~= nil and (not Clipboard.isEmpty()) and Clipboard.Type == 'text' + end + }, + { + Separator = true, + }, + { + Title = 'Select All', + Click = function() + Current.Selection = {1, #Current.Document.TextInput.Value:gsub('\n','')} + end, + Keys = { + keys.leftCtrl, + keys.a + }, + Enabled = function() + return Current.Document ~= nil + end + } + }, self, true) + else + Current.Menu = nil + end + return true + end, 'Edit', colours.lightGrey, false) + }) +end + +function LoadMenuBar() + Current.MenuBar = MenuBar:Initialise({ + Button:Initialise(1, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) + if toggle then + Menu:New(1, 2, { + { + Title = "New...", + Click = function() + Current.Document = Document:Initialise('') + end, + Keys = { + keys.leftCtrl, + keys.n + } + }, + { + Title = 'Open...', + Click = function() + DisplayOpenDocumentWindow() + end, + Keys = { + keys.leftCtrl, + keys.o + } + }, + { + Separator = true + }, + { + Title = 'Save...', + Click = function() + SaveDocument() + end, + Keys = { + keys.leftCtrl, + keys.s + }, + Enabled = function() + return Current.Document ~= nil + end + }, + { + Separator = true + }, + { + Title = 'Print...', + Click = function() + PrintDocument() + end, + Keys = { + keys.leftCtrl, + keys.p + }, + Enabled = function() + return true + end + }, + { + Separator = true + }, + { + Title = 'Quit', + Click = function() + if Close() and OneOS then + OneOS.Close() + end + end, + Keys = { + keys.leftCtrl, + keys.q + } + }, + --[[ + { + Title = 'Save As...', + Click = function() + + end + } + ]]-- + }, self, true) + else + Current.Menu = nil + end + return true + end, 'File', colours.lightGrey, false), + Button:Initialise(7, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) + if not self.Toggle then + Menu:New(7, 2, { + --[[ + { + Title = "Undo", + Click = function() + end, + Keys = { + keys.leftCtrl, + keys.z + }, + Enabled = function() + return false + end + }, + { + Title = 'Redo', + Click = function() + + end, + Keys = { + keys.leftCtrl, + keys.y + }, + Enabled = function() + return false + end + }, + { + Separator = true + }, + ]]-- + { + Title = 'Cut', + Click = function() + Clipboard.Cut(Current.Document.TextInput:Extract(true), 'text') + end, + Keys = { + keys.leftCtrl, + keys.x + }, + Enabled = function() + return Current.Document ~= nil and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil + end + }, + { + Title = 'Copy', + Click = function() + Clipboard.Copy(Current.Document.TextInput:Extract(), 'text') + end, + Keys = { + keys.leftCtrl, + keys.c + }, + Enabled = function() + return Current.Document ~= nil and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil + end + }, + { + Title = 'Paste', + Click = function() + local paste = Clipboard.Paste() + Current.Document.TextInput:Insert(paste) + Current.Document.TextInput.CursorPos = Current.Document.TextInput.CursorPos + #paste - 1 + end, + Keys = { + keys.leftCtrl, + keys.v + }, + Enabled = function() + return Current.Document ~= nil and (not Clipboard.isEmpty()) and Clipboard.Type == 'text' + end + }, + { + Separator = true, + }, + { + Title = 'Select All', + Click = function() + Current.Selection = {1, #Current.Document.TextInput.Value:gsub('\n','')} + end, + Keys = { + keys.leftCtrl, + keys.a + }, + Enabled = function() + return Current.Document ~= nil + end + } + }, self, true) + else + Current.Menu = nil + end + return true + end, 'Edit', colours.lightGrey, false), + Button:Initialise(13, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) + if not self.Toggle then + Menu:New(13, 2, { + { + Title = 'Red', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.red, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Orange', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.orange, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Yellow', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.yellow, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Pink', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.pink, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Magenta', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.magenta, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Purple', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.purple, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Light Blue', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.lightBlue, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Cyan', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.cyan, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Blue', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.blue, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Green', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.green, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Light Grey', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.lightGrey, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Grey', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.grey, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Black', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.black, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + }, + { + Title = 'Brown', + Click = function(item) + Current.Document:SetSelectionColour(item.Colour) + end, + Colour = colours.brown, + Enabled = function() + return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil) + end + } + }, self, true) + else + Current.Menu = nil + end + return true + end, 'Colour', colours.lightGrey, false) + }) +end + +function SplashScreen() + local w = colours.white + local b = colours.black + local u = colours.blue + local lb = colours.lightBlue + local splashIcon = {{w,w,w,b,w,w,w,b,w,w,w,},{w,w,w,b,w,w,w,b,w,w,w,},{w,w,w,b,u,u,u,b,w,w,w,},{w,b,b,u,u,u,u,u,b,b,w,},{b,u,u,lb,lb,u,u,u,u,u,b,},{b,u,lb,lb,u,u,u,u,u,u,b,},{b,u,lb,lb,u,u,u,u,u,u,b,},{b,u,u,u,u,u,u,u,u,u,b,},{w,b,b,b,b,b,b,b,b,b,w,}, + ["text"]={{" "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," ","I","n","k"," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," ",},{" "," ","b","y"," ","o","e","e","d"," "," "},{" "," "," "," "," "," "," "," "," "," "," ",},}, + ["textcol"]={{w,w,w,w,w,w,w,w,w,w,w,},{w,w,w,w,w,w,w,w,w,w,w,},{w,w,w,w,w,w,w,w,w,w,w,},{w,w,w,w,w,w,w,w,w,w,w,},{w,w,w,w,w,w,w,w,w,w,w,},{w,w,w,w,w,w,w,w,w,w,w,},{w,w,w,w,w,w,w,w,w,w,w,},{lb,lb,lb,lb,lb,lb,lb,lb,lb,lb,lb,},{w,w,w,w,w,w,w,w,w,w,w,},},} + Drawing.Clear(colours.white) + Drawing.DrawImage((Drawing.Screen.Width - 11)/2, (Drawing.Screen.Height - 9)/2, splashIcon, 11, 9) + Drawing.DrawBuffer() + Drawing.Clear(colours.black) + parallel.waitForAny(function()sleep(1)end, function()os.pullEvent('mouse_click')end) +end + +function Initialise(arg) + if OneOS then + fs = OneOS.FS + end + + if not OneOS then + SplashScreen() + end + EventRegister('mouse_click', TryClick) + EventRegister('mouse_drag', function(event, side, x, y)TryClick(event, side, x, y, true)end) + EventRegister('mouse_scroll', Scroll) + EventRegister('key', HandleKey) + EventRegister('char', HandleKey) + EventRegister('timer', Timer) + EventRegister('terminate', function(event) if Close() then error( "Terminated", 0 ) end end) + + LoadMenuBar() + + --Current.Document = Document:Initialise('abcdefghijklmnopqrtuvwxy')--'Hello everybody!') + if tArgs[1] then + if fs.exists(tArgs[1]) then + OpenDocument(tArgs[1]) + else + --new + end + else + Current.Document = Document:Initialise('')--'Hello everybody!') + end + + --[[ + if arg and fs.exists(arg) then + OpenDocument(arg) + else + DisplayNewDocumentWindow() + end + ]]-- + Draw() + + EventHandler() +end + +local isControlPushed = false +controlPushedTimer = nil +closeWindowTimer = nil +function Timer(event, timer) + if timer == closeWindowTimer then + if Current.Window then + Current.Window:Close() + end + Draw() + elseif timer == controlPushedTimer then + isControlPushed = false + end +end + +local ignoreNextChar = false +function HandleKey(...) + local args = {...} + local event = args[1] + local keychar = args[2] + --Mac left command character + if event == 'key' and keychar == keys.leftCtrl or keychar == keys.rightCtrl or keychar == 219 then + isControlPushed = true + controlPushedTimer = os.startTimer(0.5) + elseif isControlPushed then + if event == 'key' then + if CheckKeyboardShortcut(keychar) then + isControlPushed = false + ignoreNextChar = true + end + end + elseif ignoreNextChar then + ignoreNextChar = false + elseif Current.TextInput then + if event == 'char' then + Current.TextInput:Char(keychar) + elseif event == 'key' then + Current.TextInput:Key(keychar) + end + end +end + +function CheckKeyboardShortcut(key) + local shortcuts = {} + shortcuts[keys.n] = function() Current.Document = Document:Initialise('') end + shortcuts[keys.o] = function() DisplayOpenDocumentWindow() end + shortcuts[keys.s] = function() if Current.Document ~= nil then SaveDocument() end end + shortcuts[keys.left] = function() if Current.TextInput then Current.TextInput:Key(keys.home) end end + shortcuts[keys.right] = function() if Current.TextInput then Current.TextInput:Key(keys["end"]) end end +-- shortcuts[keys.q] = function() DisplayOpenDocumentWindow() end + if Current.Document ~= nil then + shortcuts[keys.s] = function() SaveDocument() end + shortcuts[keys.p] = function() PrintDocument() end + if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then + shortcuts[keys.x] = function() Clipboard.Cut(Current.Document.TextInput:Extract(true), 'text') end + shortcuts[keys.c] = function() Clipboard.Copy(Current.Document.TextInput:Extract(), 'text') end + end + if (not Clipboard.isEmpty()) and Clipboard.Type == 'text' then + shortcuts[keys.v] = function() local paste = Clipboard.Paste() + Current.Document.TextInput:Insert(paste) + Current.Document.TextInput.CursorPos = Current.Document.TextInput.CursorPos + #paste - 1 + end + end + shortcuts[keys.a] = function() Current.Selection = {1, #Current.Document.TextInput.Value:gsub('\n','')} end + end + + if shortcuts[key] then + shortcuts[key]() + Draw() + return true + else + return false + end +end + +--[[ + Check if the given object falls under the click coordinates +]]-- +function CheckClick(object, x, y) + if object.X <= x and object.Y <= y and object.X + object.Width > x and object.Y + object.Height > y then + return true + end +end + +--[[ + Attempt to clicka given object +]]-- +function DoClick(object, side, x, y, drag) + local obj = GetAbsolutePosition(object) + obj.Width = object.Width + obj.Height = object.Height + if object and CheckClick(obj, x, y) then + return object:Click(side, x - object.X + 1, y - object.Y + 1, drag) + end +end + +--[[ + Try to click at the given coordinates +]]-- +function TryClick(event, side, x, y, drag) + if Current.Menu then + if DoClick(Current.Menu, side, x, y, drag) then + Draw() + return + else + if Current.Menu.Owner and Current.Menu.Owner.Toggle then + Current.Menu.Owner.Toggle = false + end + Current.Menu = nil + Draw() + return + end + elseif Current.Window then + if DoClick(Current.Window, side, x, y, drag) then + Draw() + return + else + Current.Window:Flash() + return + end + end + local interfaceElements = {} + + table.insert(interfaceElements, Current.MenuBar) + table.insert(interfaceElements, Current.ScrollBar) + for i, page in ipairs(Current.Document.Pages) do + table.insert(interfaceElements, page) + end + + for i, object in ipairs(interfaceElements) do + if DoClick(object, side, x, y, drag) then + Draw() + return + end + end + Draw() +end + +function Scroll(event, direction, x, y) + if Current.Window and Current.Window.OpenButton then + Current.Document.Scroll = Current.Document.Scroll + direction + if Current.Window.Scroll < 0 then + Current.Window.Scroll = 0 + elseif Current.Window.Scroll > Current.Window.MaxScroll then + Current.Window.Scroll = Current.Window.MaxScroll + end + Draw() + elseif Current.ScrollBar then + if Current.ScrollBar:DoScroll(direction*2) then + Draw() + end + end +end + + +--[[ + Registers functions to run on certain events +]]-- +function EventRegister(event, func) + if not Events[event] then + Events[event] = {} + end + + table.insert(Events[event], func) +end + +--[[ + The main loop event handler, runs registered event functinos +]]-- +function EventHandler() + while true do + local event, arg1, arg2, arg3, arg4 = os.pullEventRaw() + if Events[event] then + for i, e in ipairs(Events[event]) do + e(event, arg1, arg2, arg3, arg4) + end + end + end +end + + +local function Extension(path, addDot) + if not path then + return nil + elseif not string.find(fs.getName(path), '%.') then + if not addDot then + return fs.getName(path) + else + return '' + end + else + local _path = path + if path:sub(#path) == '/' then + _path = path:sub(1,#path-1) + end + local extension = _path:gmatch('%.[0-9a-z]+$')() + if extension then + extension = extension:sub(2) + else + --extension = nil + return '' + end + if addDot then + extension = '.'..extension + end + return extension:lower() + end +end + +local RemoveExtension = function(path) + if path:sub(1,1) == '.' then + return path + end + local extension = Extension(path) + if extension == path then + return fs.getName(path) + end + return string.gsub(path, extension, ''):sub(1, -2) +end + +local acknowledgedColour = false +function PrintDocument() + if OneOS then + OneOS.LoadAPI('/System/API/Helpers.lua') + OneOS.LoadAPI('/System/API/Peripheral.lua') + OneOS.LoadAPI('/System/API/Printer.lua') + end + + local doPrint = function() + local window = PrintDocumentWindow:Initialise():Show() + end + + if Peripheral.GetPeripheral('printer') == nil then + ButtonDialougeWindow:Initialise('No Printer Found', 'Please place a printer next to your computer. Ensure you also insert dye (left slot) and paper (top slots)', 'Ok', nil, function(window, ok) + window:Close() + end):Show() + elseif not acknowledgedColour and FindColours(Current.Document.TextInput.Value) ~= 0 then + ButtonDialougeWindow:Initialise('Important', 'Due to the way printers work, you can\'t print in more than one colour. The dye you use will be the colour of the text.', 'Ok', nil, function(window, ok) + acknowledgedColour = true + window:Close() + doPrint() + end):Show() + else + doPrint() + end +end + +function SaveDocument() + local function save() + local h = fs.open(Current.Document.Path, 'w') + if h then + if Current.Document.Format == TextFormatPlainText then + h.write(Current.Document.TextInput.Value) + else + local lines = {} + for p, page in ipairs(Current.Document.Pages) do + for i, line in ipairs(page.Lines) do + table.insert(lines, line) + end + end + h.write(textutils.serialize(lines)) + end + Current.Modified = false + else + ButtonDialougeWindow:Initialise('Error', 'An error occured while saving the file, try again.', 'Ok', nil, function(window, ok) + window:Close() + end):Show() + end + h.close() + end + + if not Current.Document.Path then + SaveDocumentWindow:Initialise(function(self, success, path) + self:Close() + if success then + local extension = '' + if Current.Document.Format == TextFormatPlainText then + extension = '.txt' + elseif Current.Document.Format == TextFormatInkText then + extension = '.ink' + end + + if path:sub(-4) ~= extension then + path = path .. extension + end + + Current.Document.Path = path + Current.Document.Title = fs.getName(path) + save() + end + if Current.Document then + Current.TextInput = Current.Document.TextInput + end + end):Show() + else + save() + end +end + +function DisplayOpenDocumentWindow() + OpenDocumentWindow:Initialise(function(self, success, path) + self:Close() + if success then + OpenDocument(path) + end + end):Show() +end + +function OpenDocument(path) + Current.Selection = nil + local h = fs.open(path, 'r') + if h then + Current.Document = Document:Initialise(h.readAll(), RemoveExtension(fs.getName(path)), path) + else + ButtonDialougeWindow:Initialise('Error', 'An error occured while opening the file, try again.', 'Ok', nil, function(window, ok) + window:Close() + if Current.Document then + Current.TextInput = Current.Document.TextInput + end + end):Show() + end + h.close() +end + +local TidyPath = function(path) + path = '/'..path + local fs = fs + if OneOS then + fs = OneOS.FS + end + if fs.isDir(path) then + path = path .. '/' + end + + path, n = path:gsub("//", "/") + while n > 0 do + path, n = path:gsub("//", "/") + end + return path +end + +OpenDocumentWindow = { + X = 1, + Y = 1, + Width = 0, + Height = 0, + CursorPos = 1, + Visible = true, + Return = nil, + OpenButton = nil, + PathTextBox = nil, + CurrentDirectory = '/', + Scroll = 0, + MaxScroll = 0, + GoUpButton = nil, + SelectedFile = '', + Files = {}, + Typed = false, + + AbsolutePosition = function(self) + return {X = self.X, Y = self.Y} + end, + + Draw = function(self) + if not self.Visible then + return + end + Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) + Drawing.DrawBlankArea(self.X, self.Y, self.Width, 3, colours.lightGrey) + Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-6, colours.white) + Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) + Drawing.DrawBlankArea(self.X, self.Y + self.Height - 5, self.Width, 5, colours.lightGrey) + self:DrawFiles() + + if (fs.exists(self.PathTextBox.TextInput.Value)) or (self.SelectedFile and #self.SelectedFile > 0 and fs.exists(self.CurrentDirectory .. self.SelectedFile)) then + self.OpenButton.TextColour = colours.black + else + self.OpenButton.TextColour = colours.lightGrey + end + + self.PathTextBox:Draw() + self.OpenButton:Draw() + self.CancelButton:Draw() + self.GoUpButton:Draw() + end, + + DrawFiles = function(self) + for i, file in ipairs(self.Files) do + if i > self.Scroll and i - self.Scroll <= 11 then + if file == self.SelectedFile then + Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.white, colours.lightBlue) + elseif string.find(file, '%.txt') or string.find(file, '%.text') or string.find(file, '%.ink') or fs.isDir(self.CurrentDirectory .. file) then + Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.black, colours.white) + else + Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.grey, colours.white) + end + end + end + self.MaxScroll = #self.Files - 11 + if self.MaxScroll < 0 then + self.MaxScroll = 0 + end + end, + + Initialise = function(self, returnFunc) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Width = 32 + new.Height = 17 + new.Return = returnFunc + new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) + new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) + new.Title = 'Open Document' + new.Visible = true + new.CurrentDirectory = '/' + new.SelectedFile = nil + if OneOS and fs.exists('/Desktop/Documents/') then + new.CurrentDirectory = '/Desktop/Documents/' + end + new.OpenButton = Button:Initialise(new.Width - 6, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) + if fs.exists(new.PathTextBox.TextInput.Value) and self.TextColour == colours.black and not fs.isDir(new.PathTextBox.TextInput.Value) then + returnFunc(new, true, TidyPath(new.PathTextBox.TextInput.Value)) + elseif new.SelectedFile and self.TextColour == colours.black and fs.isDir(new.CurrentDirectory .. new.SelectedFile) then + new:GoToDirectory(new.CurrentDirectory .. new.SelectedFile) + elseif new.SelectedFile and self.TextColour == colours.black then + returnFunc(new, true, TidyPath(new.CurrentDirectory .. '/' .. new.SelectedFile)) + end + end, 'Open', colours.black) + new.CancelButton = Button:Initialise(new.Width - 15, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) + returnFunc(new, false) + end, 'Cancel', colours.black) + new.GoUpButton = Button:Initialise(2, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) + local folderName = fs.getName(new.CurrentDirectory) + local parentDirectory = new.CurrentDirectory:sub(1, #new.CurrentDirectory-#folderName-1) + new:GoToDirectory(parentDirectory) + end, 'Go Up', colours.black) + new.PathTextBox = TextBox:Initialise(2, new.Height - 3, new.Width - 2, 1, new, new.CurrentDirectory, colours.white, colours.black) + new:GoToDirectory(new.CurrentDirectory) + return new + end, + + Show = function(self) + Current.Window = self + return self + end, + + Close = function(self) + Current.Input = nil + Current.Window = nil + self = nil + end, + + GoToDirectory = function(self, path) + path = TidyPath(path) + self.CurrentDirectory = path + self.Scroll = 0 + self.SelectedFile = nil + self.Typed = false + self.PathTextBox.TextInput.Value = path + local fs = fs + if OneOS then + fs = OneOS.FS + end + self.Files = fs.list(self.CurrentDirectory) + Draw() + end, + + Flash = function(self) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + sleep(0.15) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + end, + + Click = function(self, side, x, y) + local items = {self.OpenButton, self.CancelButton, self.PathTextBox, self.GoUpButton} + local found = false + for i, v in ipairs(items) do + if CheckClick(v, x, y) then + v:Click(side, x, y) + found = true + end + end + + if not found then + if y <= 12 then + local fs = fs + if OneOS then + fs = OneOS.FS + end + self.SelectedFile = fs.list(self.CurrentDirectory)[y-1] + self.PathTextBox.TextInput.Value = TidyPath(self.CurrentDirectory .. '/' .. self.SelectedFile) + Draw() + end + end + return true + end +} + +PrintDocumentWindow = { + X = 1, + Y = 1, + Width = 0, + Height = 0, + CursorPos = 1, + Visible = true, + Return = nil, + PrintButton = nil, + CopiesTextBox = nil, + Scroll = 0, + MaxScroll = 0, + PrinterSelectButton = nil, + Title = '', + Status = 0, --0 = neutral, 1 = good, -1 = error + StatusText = '', + SelectedPrinter = nil, + + AbsolutePosition = function(self) + return {X = self.X, Y = self.Y} + end, + + Draw = function(self) + if not self.Visible then + return + end + Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) + Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey) + Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white) + Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) + + self.PrinterSelectButton:Draw() + Drawing.DrawCharactersCenter(self.X, self.Y + self.PrinterSelectButton.Y - 2, self.Width, 1, 'Printer', colours.black, colours.white) + Drawing.DrawCharacters(self.X + self.Width - 3, self.Y + self.PrinterSelectButton.Y - 1, '\\/', colours.black, colours.lightGrey) + Drawing.DrawCharacters(self.X + 1, self.Y + self.CopiesTextBox.Y - 1, 'Copies', colours.black, colours.white) + local statusColour = colours.grey + if self.Status == -1 then + statusColour = colours.red + elseif self.Status == 1 then + statusColour = colours.green + end + Drawing.DrawCharacters(self.X + 1, self.Y + self.CopiesTextBox.Y + 1, self.StatusText, statusColour, colours.white) + + self.CopiesTextBox:Draw() + self.PrintButton:Draw() + self.CancelButton:Draw() + end, + + Initialise = function(self) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Width = 32 + new.Height = 11 + new.Return = returnFunc + new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) + new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) + new.Title = 'Print Document' + new.Visible = true + new.PrintButton = Button:Initialise(new.Width - 7, new.Height - 1, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) + local doPrint = true + if new.SelectedPrinter == nil then + local p = Peripheral.GetPeripheral('printer') + if p then + new.SelectedPrinter = p.Side + new.PrinterSelectButton.Text = p.Fullname + else + new.StatusText = 'No Connected Printer' + new.Status = -1 + doPrint = false + end + end + if doPrint then + local printer = Printer:Initialise(new.SelectedPrinter) + local err = printer:PrintLines(Current.Document.Lines, Current.Document.Title, tonumber(new.CopiesTextBox.TextInput.Value)) + if not err then + new.StatusText = 'Document Printed!' + new.Status = 1 + closeWindowTimer = os.startTimer(1) + else + new.StatusText = err + new.Status = -1 + end + end + end, 'Print', colours.black) + new.CancelButton = Button:Initialise(new.Width - 15, new.Height - 1, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) + new:Close() + Draw() + end, 'Close', colours.black) + new.PrinterSelectButton = Button:Initialise(2, 4, new.Width - 2, nil, colours.lightGrey, new, function(self, side, x, y, toggle) + local printers = { + { + Title = "Automatic", + Click = function() + new.SelectedPrinter = nil + new.PrinterSelectButton.Text = 'Automatic' + end + }, + { + Separator = true + } + } + for i, p in ipairs(Peripheral.GetPeripherals('printer')) do + table.insert(printers, { + Title = p.Fullname, + Click = function(self) + new.SelectedPrinter = p.Side + new.PrinterSelectButton.Text = p.Fullname + end + }) + end + Current.Menu = Menu:New(x, y+4, printers, self, true) + end, 'Automatic', colours.black) + new.CopiesTextBox = TextBox:Initialise(9, 6, 4, 1, new, 1, colours.lightGrey, colours.black, nil, true) + Current.TextInput = new.CopiesTextBox.TextInput + new.StatusText = 'Waiting...' + new.Status = 0 + return new + end, + + Show = function(self) + Current.Window = self + return self + end, + + Close = function(self) + Current.Input = nil + Current.Window = nil + self = nil + end, + + Flash = function(self) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + sleep(0.15) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + end, + + Click = function(self, side, x, y) + local items = {self.PrintButton, self.CancelButton, self.CopiesTextBox, self.PrinterSelectButton} + for i, v in ipairs(items) do + if CheckClick(v, x, y) then + v:Click(side, x, y) + end + end + return true + end +} + +SaveDocumentWindow = { + X = 1, + Y = 1, + Width = 0, + Height = 0, + CursorPos = 1, + Visible = true, + Return = nil, + SaveButton = nil, + PathTextBox = nil, + CurrentDirectory = '/', + Scroll = 0, + MaxScroll = 0, + ScrollBar = nil, + GoUpButton = nil, + Files = {}, + Typed = false, + + AbsolutePosition = function(self) + return {X = self.X, Y = self.Y} + end, + + Draw = function(self) + if not self.Visible then + return + end + Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) + Drawing.DrawBlankArea(self.X, self.Y, self.Width, 3, colours.lightGrey) + Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-6, colours.white) + Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) + Drawing.DrawBlankArea(self.X, self.Y + self.Height - 5, self.Width, 5, colours.lightGrey) + Drawing.DrawCharacters(self.X + 1, self.Y + self.Height - 5, self.CurrentDirectory, colours.grey, colours.lightGrey) + self:DrawFiles() + + if (self.PathTextBox.TextInput.Value) then + self.SaveButton.TextColour = colours.black + else + self.SaveButton.TextColour = colours.lightGrey + end + + self.PathTextBox:Draw() + self.SaveButton:Draw() + self.CancelButton:Draw() + self.GoUpButton:Draw() + end, + + DrawFiles = function(self) + for i, file in ipairs(self.Files) do + if i > self.Scroll and i - self.Scroll <= 10 then + if file == self.SelectedFile then + Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.white, colours.lightBlue) + elseif fs.isDir(self.CurrentDirectory .. file) then + Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.black, colours.white) + else + Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.lightGrey, colours.white) + end + end + end + self.MaxScroll = #self.Files - 11 + if self.MaxScroll < 0 then + self.MaxScroll = 0 + end + end, + + Initialise = function(self, returnFunc) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Width = 32 + new.Height = 16 + new.Return = returnFunc + new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) + new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) + new.Title = 'Save Document' + new.Visible = true + new.CurrentDirectory = '/' + if OneOS and fs.exists('/Desktop/Documents/') then + new.CurrentDirectory = '/Desktop/Documents/' + end + new.SaveButton = Button:Initialise(new.Width - 6, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) + if self.TextColour == colours.black and not fs.isDir(new.CurrentDirectory ..'/' .. new.PathTextBox.TextInput.Value) then + returnFunc(new, true, TidyPath(new.CurrentDirectory ..'/' .. new.PathTextBox.TextInput.Value)) + elseif new.SelectedFile and self.TextColour == colours.black and fs.isDir(new.CurrentDirectory .. new.SelectedFile) then + new:GoToDirectory(new.CurrentDirectory .. new.SelectedFile) + end + end, 'Save', colours.black) + new.CancelButton = Button:Initialise(new.Width - 15, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) + returnFunc(new, false) + end, 'Cancel', colours.black) + new.GoUpButton = Button:Initialise(2, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) + local folderName = fs.getName(new.CurrentDirectory) + local parentDirectory = new.CurrentDirectory:sub(1, #new.CurrentDirectory-#folderName-1) + new:GoToDirectory(parentDirectory) + end, 'Go Up', colours.black) + new.PathTextBox = TextBox:Initialise(2, new.Height - 3, new.Width - 2, 1, new, '', colours.white, colours.black, function(key) + if key == keys.enter then + new.SaveButton:Click() + end + end) + new.PathTextBox.Placeholder = 'Document Name' + Current.TextInput = new.PathTextBox.TextInput + new:GoToDirectory(new.CurrentDirectory) + return new + end, + + Show = function(self) + Current.Window = self + return self + end, + + Close = function(self) + Current.Input = nil + Current.Window = nil + self = nil + end, + + GoToDirectory = function(self, path) + path = TidyPath(path) + self.CurrentDirectory = path + self.Scroll = 0 + self.Typed = false + local fs = fs + if OneOS then + fs = OneOS.FS + end + self.Files = fs.list(self.CurrentDirectory) + Draw() + end, + + Flash = function(self) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + sleep(0.15) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + end, + + Click = function(self, side, x, y) + local items = {self.SaveButton, self.CancelButton, self.PathTextBox, self.GoUpButton} + local found = false + for i, v in ipairs(items) do + if CheckClick(v, x, y) then + v:Click(side, x, y) + found = true + end + end + + if not found then + if y <= 11 then + local files = fs.list(self.CurrentDirectory) + if files[y-1] then + self:GoToDirectory(self.CurrentDirectory..files[y-1]) + Draw() + end + end + end + return true + end +} + +local WrapText = function(text, maxWidth) + local lines = {''} + for word, space in text:gmatch('(%S+)(%s*)') do + local temp = lines[#lines] .. word .. space:gsub('\n','') + if #temp > maxWidth then + table.insert(lines, '') + end + if space:find('\n') then + lines[#lines] = lines[#lines] .. word + + space = space:gsub('\n', function() + table.insert(lines, '') + return '' + end) + else + lines[#lines] = lines[#lines] .. word .. space + end + end + return lines +end + +ButtonDialougeWindow = { + X = 1, + Y = 1, + Width = 0, + Height = 0, + CursorPos = 1, + Visible = true, + CancelButton = nil, + OkButton = nil, + Lines = {}, + + AbsolutePosition = function(self) + return {X = self.X, Y = self.Y} + end, + + Draw = function(self) + if not self.Visible then + return + end + Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) + Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey) + Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white) + Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) + + for i, text in ipairs(self.Lines) do + Drawing.DrawCharacters(self.X + 1, self.Y + 1 + i, text, colours.black, colours.white) + end + + self.OkButton:Draw() + if self.CancelButton then + self.CancelButton:Draw() + end + end, + + Initialise = function(self, title, message, okText, cancelText, returnFunc) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Width = 28 + new.Lines = WrapText(message, new.Width - 2) + new.Height = 5 + #new.Lines + new.Return = returnFunc + new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) + new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) + new.Title = title + new.Visible = true + new.Visible = true + new.OkButton = Button:Initialise(new.Width - #okText - 2, new.Height - 1, nil, 1, nil, new, function() + returnFunc(new, true) + end, okText) + if cancelText then + new.CancelButton = Button:Initialise(new.Width - #okText - 2 - 1 - #cancelText - 2, new.Height - 1, nil, 1, nil, new, function() + returnFunc(new, false) + end, cancelText) + end + + return new + end, + + Show = function(self) + Current.Window = self + return self + end, + + Close = function(self) + Current.Window = nil + self = nil + end, + + Flash = function(self) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + sleep(0.15) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + end, + + Click = function(self, side, x, y) + local items = {self.OkButton, self.CancelButton} + local found = false + for i, v in ipairs(items) do + if CheckClick(v, x, y) then + v:Click(side, x, y) + found = true + end + end + return true + end +} + +TextDialougeWindow = { + X = 1, + Y = 1, + Width = 0, + Height = 0, + CursorPos = 1, + Visible = true, + CancelButton = nil, + OkButton = nil, + Lines = {}, + TextInput = nil, + + AbsolutePosition = function(self) + return {X = self.X, Y = self.Y} + end, + + Draw = function(self) + if not self.Visible then + return + end + Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) + Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey) + Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white) + Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) + + for i, text in ipairs(self.Lines) do + Drawing.DrawCharacters(self.X + 1, self.Y + 1 + i, text, colours.black, colours.white) + end + + + Drawing.DrawBlankArea(self.X + 1, self.Y + self.Height - 4, self.Width - 2, 1, colours.lightGrey) + Drawing.DrawCharacters(self.X + 2, self.Y + self.Height - 4, self.TextInput.Value, colours.black, colours.lightGrey) + Current.CursorPos = {self.X + 2 + self.TextInput.CursorPos, self.Y + self.Height - 4} + Current.CursorColour = colours.black + + self.OkButton:Draw() + if self.CancelButton then + self.CancelButton:Draw() + end + end, + + Initialise = function(self, title, message, okText, cancelText, returnFunc, numerical) + local new = {} -- the new instance + setmetatable( new, {__index = self} ) + new.Width = 28 + new.Lines = WrapText(message, new.Width - 2) + new.Height = 7 + #new.Lines + new.Return = returnFunc + new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) + new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) + new.Title = title + new.Visible = true + new.Visible = true + new.OkButton = Button:Initialise(new.Width - #okText - 2, new.Height - 1, nil, 1, nil, new, function() + if #new.TextInput.Value > 0 then + returnFunc(new, true, new.TextInput.Value) + end + end, okText) + if cancelText then + new.CancelButton = Button:Initialise(new.Width - #okText - 2 - 1 - #cancelText - 2, new.Height - 1, nil, 1, nil, new, function() + returnFunc(new, false) + end, cancelText) + end + new.TextInput = TextInput:Initialise('', function(enter) + if enter then + new.OkButton:Click() + end + Draw() + end, numerical) + + Current.Input = new.TextInput + + return new + end, + + Show = function(self) + Current.Window = self + return self + end, + + Close = function(self) + Current.Window = nil + Current.Input = nil + self = nil + end, + + Flash = function(self) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + sleep(0.15) + self.Visible = false + Draw() + sleep(0.15) + self.Visible = true + Draw() + end, + + Click = function(self, side, x, y) + local items = {self.OkButton, self.CancelButton} + local found = false + for i, v in ipairs(items) do + if CheckClick(v, x, y) then + v:Click(side, x, y) + found = true + end + end + return true + end +} + +function PrintCentered(text, y) + local w, h = term.getSize() + x = math.ceil(math.ceil((w / 2) - (#text / 2)), 0)+1 + term.setCursorPos(x, y) + print(text) +end + +function DoVanillaClose() + term.setBackgroundColour(colours.black) + term.setTextColour(colours.white) + term.clear() + term.setCursorPos(1, 1) + PrintCentered("Thanks for using Ink!", (Drawing.Screen.Height/2)-1) + term.setTextColour(colours.lightGrey) + PrintCentered("Word Proccessor for ComputerCraft", (Drawing.Screen.Height/2)) + term.setTextColour(colours.white) + PrintCentered("(c) oeed 2014", (Drawing.Screen.Height/2)+3) + term.setCursorPos(1, Drawing.Screen.Height) + error('', 0) +end + +function Close() + if isQuitting or not Current.Document or not Current.Modified then + if not OneOS then + DoVanillaClose() + end + return true + else + local _w = ButtonDialougeWindow:Initialise('Quit Ink?', 'You have unsaved changes, do you want to quit anyway?', 'Quit', 'Cancel', function(window, success) + if success then + if OneOS then + OneOS.Close(true) + else + DoVanillaClose() + end + end + window:Close() + Draw() + end):Show() + --it's hacky but it works + os.queueEvent('mouse_click', 1, _w.X, _w.Y) + return false + end +end + +if OneOS then + OneOS.CanClose = function() + return Close() + end +end + +Initialise() \ No newline at end of file -- cgit v1.2.3