Advertisement
Guest User

Untitled

a guest
May 27th, 2016
246
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.52 KB | None | 0 0
  1. local function split(self, sep)
  2. if sep == nil then
  3. sep = "%s"
  4. end
  5. local t={} ; i=1
  6. for str in string.gmatch(self, "([^"..sep.."]+)") do
  7. t[i] = str
  8. i = i + 1
  9. end
  10. return t
  11. end
  12. local letters = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }
  13. local lettermap = {}
  14. for i=1,#letters do
  15. lettermap[letters[i]] = i
  16. end
  17. local function isFunc(input)
  18. return input:sub(1,1) == "="
  19. end
  20. local function letterToNumber(letter)
  21. return lettermap[letter:lower()]
  22. end
  23. local function numberToLetter(number)
  24. return (letters[number]):upper()
  25. end
  26. local function parseFunc(input) --converts "=Funcname lettercodnumbercode" in {"A1", "B2"}, funcname format
  27. local cells = {}
  28. input = input:sub(2,#input)
  29. local tokens = split(input)
  30. local fname = tokens[1]
  31. for i=2, #tokens do
  32. local token = tokens[i]
  33. local letter = {""}
  34. local number = {""}
  35. local range = false
  36. local comma = false
  37. for c in token:gmatch(".") do
  38. --iterate over characters
  39. if c == "-" then
  40. range = true
  41. elseif c == "," then
  42. comma = true
  43. elseif tonumber(c) then
  44. if range or comma then
  45. number[#number+1] = range and "-" or comma and ","
  46. number[#number+1] = c
  47. range = false
  48. comma = false
  49. else
  50. number[#number] = number[#number] .. c
  51. end
  52. elseif lettermap[c:lower()] then
  53. if range or comma then
  54. letter[#letter+1] = range and "-" or comma and ","
  55. letter[#letter+1] = c
  56. range = false
  57. comma = false
  58. else
  59. letter[#letter] = letter[#letter] .. c
  60. end
  61. end
  62. end
  63. local range, l = false
  64. local nrange = false
  65. local function doNumbers()
  66. for j=1,#number do
  67. local n = number[j]
  68. if n == "," then
  69. --ignore
  70. nrange = false
  71. elseif n == "-" then
  72. nrange = true
  73. elseif nrange then
  74. for num = number[j-2] + 1, n do
  75. cells[#cells+1] = l .. num
  76. end
  77. nrange = false
  78. else
  79. cells[#cells+1] = l .. n
  80. nrange = false
  81. end
  82. end
  83. end
  84. for k=1,#letter do
  85. l = letter[k]
  86. if l == "," then
  87. --ignore
  88. range = false
  89. elseif l == "-" then
  90. range = true
  91. elseif range then
  92. local constl = l
  93. for m = letterToNumber(letter[k-2]) + 1, letterToNumber(constl) do
  94. l = numberToLetter(m)
  95. doNumbers()
  96. end
  97. range = false
  98. else
  99. range = false
  100. doNumbers()
  101. end
  102. end
  103. end
  104. return cells, fname
  105. end
  106. local function convertParsedToIndexes(parsed) --converts the output of parseFunc to {{1,1},{2,2}} format
  107. local token, letter, number
  108. local output = {}
  109. for i=1,#parsed do
  110. token = parsed[i]
  111. letter, number = "",""
  112. for c in token:gmatch(".") do
  113. if tonumber(c) then
  114. number = number .. c
  115. else
  116. letter = letter .. c
  117. end
  118. end
  119. output[i] = {letterToNumber(letter), tonumber(number)}
  120. end
  121. return output
  122. end
  123. local function myRead(oldtxt)
  124. oldtxt = oldtxt or ""
  125. term.setCursorBlink(true)
  126. local pos = #oldtxt+1
  127. local x,y = term.getCursorPos()
  128. local event, key
  129. local keep = true
  130. local handler = {
  131. key = {
  132. [keys.left] = function()
  133. pos = pos > 1 and pos - 1 or pos
  134. end;
  135. [keys.right] = function()
  136. pos = pos <= #oldtxt and pos + 1 or pos
  137. end;
  138. [keys.backspace] = function()
  139. if pos > 1 then
  140. oldtxt = oldtxt:sub(1,pos-2) .. oldtxt:sub(pos, #oldtxt)
  141. pos = pos - 1
  142. end
  143. end;
  144. [keys.delete] = function()
  145. if pos <= #oldtxt then
  146. oldtxt = oldtxt:sub(1,pos-1) .. oldtxt:sub(pos+1, #oldtxt)
  147. end
  148. end;
  149. [keys.enter] = function()
  150. keep = false
  151. end;
  152. };
  153. char = function()
  154. oldtxt = oldtxt:sub(1,pos-1) .. key .. oldtxt:sub(pos, #oldtxt)
  155. pos = pos + #key
  156. end;
  157. }
  158. handler.paste = handler.char
  159. while keep do
  160. term.setCursorPos(x,y)
  161. term.clearLine()
  162. term.write(oldtxt)
  163. term.setCursorPos(x - 1 + pos, y)
  164. event, key = os.pullEvent()
  165. if handler[event] then
  166. if type(handler[event]) == "table" and handler[event][key] then
  167. handler[event][key]()
  168. elseif type(handler[event]) == "function" then
  169. handler[event]()
  170. end
  171. end
  172. end
  173. term.setCursorBlink(false)
  174. return oldtxt
  175. end
  176. --Setup vars:
  177. --general vars
  178. local filepath = ""
  179. --display vars
  180. local scroll = {0,0}
  181. local showncolumncount = 10
  182. local w,h = term.getSize()
  183. local gridw, gridh = w-2, h-2 --why not put this here :P
  184. local gridx, gridy = 4,2
  185. local defaultColumnWidth = 10
  186. --selection vars
  187. local selected = {1,1} --selected cell position
  188. local endselection = {}
  189. local multiselect = false
  190. --file content vars
  191. local grid = {}
  192. local columnWidths = {}
  193. --copy/paste vars
  194. local clipboard = {}
  195. --function vars
  196. local precalculated = {}
  197. --theme vars
  198. local theme = term.isColor() and {
  199. default = {colors.white, colors.black},
  200. selected = {colors.green, colors.white},
  201. multiselected = {colors.lime, colors.black},
  202. menu = {colors.green, colors.white},
  203. border = {colors.lightGray, colors.black}
  204. } or {
  205. default = {colors.black, colors.white},
  206. selected = {colors.white, colors.black},
  207. multiselected = {colors.gray, colors.black},
  208. menu = {colors.white, colors.black},
  209. border = {colors.lightGray, colors.black}
  210. }
  211. --Handle args:
  212. local tArgs = {...}
  213. if #tArgs < 1 then --not enough args
  214. print[[
  215. Usage:
  216. speadersheet <filename>
  217. ]]
  218. error("",0)
  219. end
  220. filepath = tArgs[1]
  221. --Local functions:
  222. --[[
  223. Speadersheet function functions + formatting
  224. ]]
  225. local function mytonumber(input)
  226. return tonumber(input) or 0
  227. end
  228. local spfunctions = {
  229. ADD = function(currently, nextnum)
  230. return mytonumber(currently) + mytonumber(nextnum)
  231. end;
  232. COPY = function(currently) return currently end;
  233. SUB = function(currently, nextnum)
  234. return mytonumber(currently) - mytonumber(nextnum)
  235. end;
  236. MUL = function(currently, nextnum)
  237. return mytonumber(currently) * mytonumber(nextnum)
  238. end;
  239. DIV = function(currently, nextnum)
  240. return mytonumber(currently) / mytonumber(nextnum)
  241. end;
  242. POW = function(currently, nextnum)
  243. return mytonumber(currently) ^ mytonumber(nextnum)
  244. end;
  245. COUNT = function(currently, nextnum)
  246. return mytonumber(currently) + 1
  247. end;
  248. }
  249. local spfuncinit = {
  250. COUNT = function() return 1 end;
  251. }
  252. local function doFunction(name, indexes)
  253. if spfunctions[name] then
  254. local func = spfunctions[name]
  255. local current, output
  256. for i=1, #indexes do
  257. local cx,cy = indexes[i][1], indexes[i][2]
  258. if precalculated[cx] and precalculated[cx][cy] then
  259. current = precalculated[cx][cy]
  260. else
  261. current = grid[cx] and grid[cx][cy] and grid[cx][cy] or ""
  262. end
  263. if i > 1 then
  264. output = func(output, current)
  265. elseif spfuncinit[name] then
  266. output = spfuncinit[name](current)
  267. else
  268. output = current
  269. end
  270. end
  271. return output
  272. else
  273. return "ERR:" .. name
  274. end
  275. end
  276. local function formatText(text, cwidth, x, y)
  277. if precalculated[x] and precalculated[x][y] then
  278. text = precalculated[x][y]
  279. end
  280. while isFunc(text) or text:sub(1,1) == "{" do
  281. local parsed, func
  282. if isFunc(text) then
  283. parsed, func = parseFunc(text)
  284. parsed = convertParsedToIndexes(parsed)
  285. else
  286. local tbl = textutils.unserialize(text)
  287. func = tbl[1]
  288. parsed = tbl[2]
  289. end
  290. text = tostring(doFunction(func, parsed))
  291. if not isFunc(text) and x and y then
  292. if not precalculated[x] then
  293. precalculated[x] = {}
  294. end
  295. precalculated[x][y] = text
  296. end
  297. end
  298. if #text >= cwidth then
  299. return text:sub(1,cwidth)
  300. elseif tonumber(text) then
  301. return string.rep(" ",(cwidth) - #text) .. text
  302. else
  303. return text .. string.rep(" ",(cwidth) - #text)
  304. end
  305. end
  306. --[[
  307. Other functions
  308. ]]
  309. local function selectColors(name) --selects colors, args: name of theme
  310. local cols = theme[name]
  311. term.setBackgroundColor(cols[1])
  312. term.setTextColor(cols[2])
  313. end
  314. local function redrawGrid() --redraws grid and bottom line
  315. --this is the function that will need lots of optimization probably
  316. local drawing = {scroll[1], scroll[2]} --which cell are we drawing (to get the content)
  317. for y=gridy, gridh + gridy - 1 do
  318. drawing[2] = drawing[2] + 1
  319. drawing[1] = scroll[1]
  320. term.setCursorPos(gridx, y)
  321. for i=1,showncolumncount do
  322. drawing[1] = drawing[1] + 1
  323. local cwidth = columnWidths[drawing[1]] or defaultColumnWidth
  324. selectColors((selected[1] == drawing[1] and selected[2] == drawing[2]) and "selected" or (multiselect and (selected[1] >= drawing[1] and selected[2] >= drawing[2]) and (endselection[1] <= drawing[1] and endselection[2] <= drawing[2])) and "multiselected" or "default")
  325. local realtext = grid[drawing[1]] and grid[drawing[1]][drawing[2]] and grid[drawing[1]][drawing[2]] or ""
  326. local text = formatText(realtext, cwidth - 1, drawing[1], drawing[2])
  327. term.write(text)
  328. if selected[1] == drawing[1] and selected[2] == drawing[2] then
  329. local oldx,oldy = term.getCursorPos()
  330. term.setCursorPos(1,h)
  331. selectColors("menu")
  332. --This is repeated because of functions later
  333. term.write(realtext .. (#realtext < w and string.rep(" ",w - #realtext) or ""))
  334. term.setCursorPos(oldx,oldy)
  335. end
  336. selectColors("border")
  337. term.write(" ")
  338. end
  339. end
  340. end
  341. local function redrawMenu() --redraws column and row numbers
  342. --redraw top column numbers
  343. do
  344. local drawing = scroll[1]
  345. term.setCursorPos(gridx, 1)
  346. selectColors("menu")
  347. term.clearLine()
  348. term.setCursorPos(gridx, 1)
  349. for i=1,showncolumncount do
  350. drawing = drawing + 1
  351. local cwidth = columnWidths[drawing] or defaultColumnWidth
  352. term.write(numberToLetter(drawing))
  353. term.write(string.rep(" ",cwidth - #numberToLetter(drawing)))
  354. end
  355. end
  356. --redraw line numbers
  357. local drawing = scroll[2]
  358. for y=gridy, gridh + gridy - 1 do
  359. drawing = drawing + 1
  360. term.setCursorPos(1, y)
  361. term.write(tostring(drawing) .. string.rep(" ",3 - #tostring(drawing)))
  362. end
  363. end
  364. local function checkScroll() --scrolls if needed
  365. local scrolled = false
  366. --horizontal:
  367. local cx = gridx
  368. local count = 0
  369. local drawing = scroll[1]
  370. for i=1,w do -- count how many columns are on screen
  371. drawing = drawing + 1
  372. local cwidth = columnWidths[drawing] or defaultColumnWidth
  373. cx = cx + cwidth
  374. count = count + 1
  375. if cx >= w then break end
  376. end
  377. showncolumncount = count
  378. while selected[1] - scroll[1] < 1 do
  379. scroll[1] = scroll[1] - 1
  380. scrolled = true
  381. end
  382. while selected[1] > scroll[1] + count do
  383. scroll[1] = scroll[1] + 1
  384. scrolled = true
  385. end
  386. --vertical:
  387. while selected[2] - scroll[2] < 1 do
  388. scroll[2] = scroll[2] - 1
  389. scrolled = true
  390. end
  391. while selected[2] > scroll[2] + gridh do
  392. scroll[2] = scroll[2] + 1
  393. scrolled = true
  394. end
  395. if scrolled then
  396. redrawGrid()
  397. redrawMenu()
  398. end
  399. end
  400. local function save() --saves persistant data
  401. local h = fs.open(filepath,"w")
  402. local cont = textutils.serialize{
  403. grid,
  404. selected,
  405. endselection,
  406. multiselect,
  407. columnWidths,
  408. "placeholder",
  409. scroll
  410. }
  411. h.write(cont)
  412. h.close()
  413. end
  414. local function reload() --loads persistant data
  415. if fs.exists(filepath) then
  416. local h = fs.open(filepath,"r")
  417. local cont = textutils.unserialize(h.readAll())
  418. h.close()
  419. grid = cont[1] or {}
  420. selected = cont[2] or {1,1}
  421. endselection = cont[3] or {1,1}
  422. multiselect = cont[4]
  423. columnWidths = cont[5] or {}
  424. scroll = cont[7] or {0,0}
  425. end
  426. end
  427. local function cancel() --cancels multiselection
  428. if multiselect then
  429. multiselect = false
  430. end
  431. endselection = {selected[1], selected[2]}
  432. end
  433. local function initMultiselect() --inits multiselection if needed
  434. if not multiselect then
  435. multiselect = true
  436. endselection = {selected[1], selected[2]}
  437. end
  438. end
  439. local function convertSelection() -- converts multi/single selection to a smaller sub grid and returns it
  440. local subx, suby = 1,1
  441. local subgrid = {}
  442. for x = endselection[1], selected[1] do
  443. subgrid[subx] = {}
  444. for y = endselection[2], selected[2] do
  445. subgrid[subx][suby] = grid[x] and grid[x][y] and grid[x][y] or ""
  446. suby = suby + 1
  447. end
  448. suby = 1
  449. subx = subx + 1
  450. end
  451. return subgrid
  452. end
  453. local function pasteSubgrid(subgrid, x, y) --pastes subgrid in main grid
  454. for i=#subgrid, 1, -1 do
  455. local line = subgrid[i]
  456. for j = #line, 1, -1 do
  457. local cell = line[j]
  458. if not grid[x + i - 1] then
  459. grid[x + i - 1] = {}
  460. end
  461. grid[x + i - 1][y + j - 1] = cell
  462. end
  463. end
  464. precalculated = {}
  465. end
  466. local function deleteSelection()
  467. for x = endselection[1], selected[1] do
  468. for y = endselection[2], selected[2] do
  469. if grid[x] then
  470. grid[x][y] = ""
  471. end
  472. end
  473. end
  474. precalculated = {}
  475. end
  476. local function copyCell(bx,by,ex,ey)
  477. if not grid[ex] then
  478. grid[ex] = {}
  479. end
  480. local txt
  481. if grid[bx] and grid[bx][by] and isFunc(grid[bx][by]) or grid[bx][by]:sub(1,1) == "{" then
  482. local result, func
  483. if isFunc(grid[bx][by]) then
  484. result, func = parseFunc(grid[bx][by])
  485. result = convertParsedToIndexes(result)
  486. else
  487. func, result = unpack(textutils.unserialize(grid[bx][by]))
  488. end
  489. local t
  490. for i=1, #result do
  491. t = result[i]
  492. t[1] = t[1] + ex - bx
  493. t[2] = t[2] + ey - by
  494. end
  495. txt = textutils.serialize {func, result}
  496. else
  497. txt = grid[bx] and grid[bx][by] or ""
  498. end
  499. grid[ex][ey] = txt
  500. precalculated = {}
  501. end
  502. reload()
  503. --Key-event table:
  504. local handler = {
  505. --[[
  506. movement:
  507. arrow keys = move
  508. p/l = multiselect (move right bottom corner right/down)
  509. enter = edit
  510. home = first column
  511. end = first row
  512. page up = page up
  513. page down = page down
  514. general:
  515. s = save
  516. q = exit
  517. copy/paste:
  518. c = copy
  519. v = paste
  520. x = cut
  521. n = make column less wide
  522. m = make column more wide
  523. u/h/j/k = copy one cell ^/</v/>
  524. ]]
  525. key = {
  526. [keys.up] = function() cancel() selected[2] = selected[2] > 1 and selected[2] - 1 or 1 redrawGrid() end;
  527. [keys.down] = function() cancel() selected[2] = selected[2] + 1 redrawGrid() end;
  528. [keys.left] = function() cancel() selected[1] = selected[1] > 1 and selected[1] - 1 or 1 redrawGrid() end;
  529. [keys.right] = function() cancel() selected[1] = selected[1] + 1 redrawGrid() end;
  530. [keys.home] = function() cancel() selected[1] = 1 redrawGrid() end;
  531. [keys["end"]] = function() cancel() selected[2] = 1 redrawGrid() end;
  532. [keys.pageUp] = function() cancel() selected[2] = selected[2] > gridh-1 and selected[2] - (gridh-1) or 1 redrawGrid() end;
  533. [keys.pageDown] = function() cancel() selected[2] = selected[2] + gridh - 1 redrawGrid() end;
  534. [keys.p] = function() initMultiselect() selected[1] = selected[1] + 1 redrawGrid() end;
  535. [keys.l] = function() initMultiselect() selected[2] = selected[2] + 1 redrawGrid() end;
  536. [keys.enter] = function()
  537. term.setCursorPos(1,h)
  538. selectColors("menu")
  539. term.clearLine()
  540. term.setCursorPos(1,h)
  541. if not grid[selected[1]] then
  542. grid[selected[1]] = {}
  543. end
  544. grid[selected[1]][selected[2]] = myRead(grid[selected[1]][selected[2]] and grid[selected[1]][selected[2]] or "")
  545. precalculated = {}
  546. redrawGrid()
  547. redrawMenu()
  548. end;
  549. [keys.s] = function()
  550. save()
  551. end;
  552. [keys.q] = function()
  553. term.setBackgroundColor(colors.black)
  554. term.setTextColor(colors.white)
  555. term.clear()
  556. term.setCursorPos(1,1)
  557. sleep(0)
  558. error("",0)
  559. end;
  560. [keys.c] = function()
  561. clipboard = convertSelection()
  562. end;
  563. [keys.v] = function()
  564. pasteSubgrid(clipboard, selected[1], selected[2])
  565. redrawGrid()
  566. end;
  567. [keys.x] = function()
  568. clipboard = convertSelection()
  569. deleteSelection()
  570. redrawGrid()
  571. end;
  572. [keys.n] = function()
  573. local neww = (columnWidths[selected[1]] or defaultColumnWidth) - 1
  574. columnWidths[selected[1]] = neww > 0 and neww or 1
  575. redrawGrid()
  576. redrawMenu()
  577. end;
  578. [keys.m] = function()
  579. local neww = (columnWidths[selected[1]] or defaultColumnWidth) + 1
  580. columnWidths[selected[1]] = neww < gridw and neww or gridw
  581. redrawGrid()
  582. redrawMenu()
  583. end;
  584. }
  585. }
  586. handler.key[keys.u] = function()
  587. if selected[2] == 1 then return end
  588. copyCell(selected[1], selected[2], selected[1], selected[2] - 1)
  589. handler["key"][keys.up]()
  590. end;
  591. handler.key[keys.h] = function()
  592. if selected[1] == 1 then return end
  593. copyCell(selected[1], selected[2], selected[1] - 1, selected[2])
  594. handler["key"][keys.left]()
  595. end;
  596. handler.key[keys.j] = function()
  597. copyCell(selected[1], selected[2], selected[1], selected[2] + 1)
  598. handler["key"][keys.down]()
  599. end;
  600. handler.key[keys.k] = function()
  601. copyCell(selected[1], selected[2], selected[1] + 1, selected[2])
  602. handler["key"][keys.right]()
  603. end;
  604. --Main loop:
  605. term.clear()
  606. redrawGrid()
  607. redrawMenu()
  608. while true do
  609. local event,param = os.pullEvent()
  610. if handler[event] and handler[event][param] then
  611. handler[event][param]()
  612. checkScroll()
  613. end
  614. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement