· 6 years ago · Jan 31, 2020, 01:16 AM
1--
2-- Component Methods Browser
3-- Platform: OpenComputers
4--
5-- Author: Sharidan
6-- www.sharidan.dk
7--
8-- You may freely use and distribute this script
9-- as long as the original author information
10-- is left as is.
11-- Please give credit where credit is due :)
12--
13local app = {
14 name = "Component Methods Browser",
15 version = "1.1-i",
16}
17
18-- Display color theme
19local theme = {
20 titleBar = {
21 "black", "lightBlue",
22 },
23 desktop = {
24 "white", "gray",
25 },
26 desktopError = {
27 "red", "gray",
28 },
29 desktopWarning = {
30 "yellow", "gray",
31 },
32 desktopAccept = {
33 "lime", "gray",
34 },
35 desktopGray = {
36 "silver", "gray",
37 },
38 highlight = {
39 "lightBlue", "gray",
40 },
41 menu = {
42 normal = {
43 "black", "lightBlue",
44 },
45 highlight = {
46 "black", "orange",
47 },
48 },
49 lua = {
50 text = {
51 "white", "gray",
52 },
53 comment = {
54 "silver", "gray",
55 },
56 strings = {
57 "yellow", "gray",
58 },
59 numbers = {
60 "red", "gray",
61 },
62 operators = {
63 "magenta", "gray",
64 },
65 delims = {
66 "lime", "gray",
67 },
68 consts = {
69 "lightblue", "gray",
70 },
71 optionals = {
72 "silver", "gray",
73 },
74 functs = {
75 "lightblue", "gray",
76 },
77 },
78}
79
80-- Component references
81local components = require("component")
82local fs = require("filesystem")
83local event = require("event")
84local uni = require("unicode")
85local keyb = require("keyboard")
86
87-- Control flags
88local refreshComponents = true
89
90local function saveTheme()
91 if (not fs.exists("/etc/cmb.theme")) then
92 local f = io.open("/etc/cmb.theme", "w")
93 if (f) then
94 local ser = require("serialization")
95 f:write("-- Theme file for: "..app.name.."\n")
96 f:write(ser.serialize(theme))
97 f:close()
98 ser = nil
99 end
100 f = nil
101 end
102end
103
104local function loadTheme()
105 if (fs.exists("/etc/cmb.theme")) then
106 local f = io.open("/etc/cmb.theme", "r")
107 if (f) then
108 local reading = true
109 local data = nil
110 while reading do
111 local ln,_ = f:read()
112 if (ln) then
113 if (string.sub(ln, 1, 1) == "{") then
114 data = ln
115 reading = nil
116 end
117 else
118 reading = nil
119 end
120 end
121 f:close()
122 if (data) then
123 local ser = require("serialization")
124 theme = ser.unserialize(data)
125 ser = nil
126 end
127 data = nil
128 end
129 f = nil
130 else
131 saveTheme()
132 end
133end
134
135-- Snippet: term
136-- GPU wrapper that creates an alternative term object
137-- for manipulating screen contents.
138local function newTerm()
139 local this = {}
140
141 -- this._so = require("component").gpu
142 this._so = components.gpu
143 this._od = this._so.getDepth()
144 this._ow, this._oh = this._so.getResolution()
145 this.width = this._ow
146 this.height = this._oh
147 this.depth = this._od
148 this._cx, this._cy = 1, 1
149
150 function this:color(fore, back)
151 local function fixclr(c, d)
152 local cl = { "white", "orange", "magenta", "lightblue", "yellow", "lime", "pink", "gray", "silver", "cyan", "purple", "blue", "brown", "green", "red", "black" }
153 local cn = { 0xffffff, 0xffcc33, 0xcc66cc, 0x6699ff, 0xffff33, 0x33cc33, 0xff6699, 0x333333, 0xcccccc, 0x336699, 0x9933cc, 0x333399, 0x663300, 0x336600, 0xff3333, 0x000000 }
154 local nc = -1
155 if (type(c) == "string") then
156 local t = string.lower(string.gsub(c, " ", ""))
157 t = string.gsub(t, "grey", "gray")
158 t = string.gsub(t, "lightgray", "silver")
159 for i = 1, #cl do
160 if (t == cl[i]) then
161 nc = i
162 break
163 end
164 end
165 elseif (type(c) == "number") then
166 if (c >= 0 and c <= 15) then
167 nc = c + 1
168 end
169 end
170 cl = nil
171 if (d == 1 and nc > -1) then
172 -- Gray Cyan Purple Blue Brown Green Black
173 if (nc == 8 or nc == 10 or nc == 11 or nc == 12 or nc == 13 or nc == 14 or nc == 16) then
174 return cn[16] -- Black
175 else
176 return cn[1] -- White
177 end
178 elseif ((d == 4 or d == 8) and nc > -1) then
179 return cn[nc]
180 end
181 return nil
182 end
183 local d = self._so.getDepth()
184 local f, fp = fixclr(fore, d)
185 local b, bp = fixclr(back, d)
186 if (f) then
187 self._so.setForeground(f, false)
188 end
189 if (b) then
190 self._so.setBackground(b, false)
191 end
192 end
193 function this:cls(fore, back)
194 if (fore) then
195 self:color(fore, back)
196 end
197 self._so.fill(1, 1, self.width, self.height, " ")
198 self._cx = 1
199 self._cy = 1
200 end
201 function this:clearln(y, fore, back)
202 if (fore) then
203 self:color(fore, back)
204 end
205 self._so.set(1, y, string.rep(" ", self.width))
206 self._cx = 1
207 self._cy = y
208 end
209 function this:drawRect(x, y, width, height, ch)
210 self._so.fill(x, y, width, height, ch)
211 self._cx = x
212 self._cy = y
213 end
214 function this:write(text)
215 local txt = tostring(text)
216 self._so.set(self._cx, self._cy, txt)
217 self._cx = self._cx + #txt
218 end
219 function this:writeXY(x, y, text)
220 local txt = tostring(text)
221 self._so.set(x, y, txt)
222 self._cx = x + #txt
223 self._cy = y
224 end
225 function this:rightXY(x, y, text)
226 local txt = tostring(text)
227 self._so.set((x - #txt) + 1, y, txt)
228 self._cx = x + 1
229 self._cy = y
230 end
231 function this:centerY(y, text)
232 local txt = tostring(text)
233 local x = math.floor((self.width - #txt) / 2) + 1
234 self._so.set(x, y, txt)
235 self._cx = x + #txt
236 self._cy = y
237 end
238 function this:getXY()
239 return self._cx, self._cy
240 end
241 function this:gotoXY(x, y)
242 self._cx = tonumber(x)
243 self._cy = tonumber(y)
244 end
245 function this:cleartb(fore, back, tbFore, tbBack, appName, appVers)
246 if (fore) then
247 self:cls(fore, back)
248 self:clearln(1, tbFore, tbBack)
249 self:writeXY(2, 1, appName)
250 self:rightXY(self.width - 1, 1, "v"..appVers)
251 else
252 self:cls(table.unpack(theme.desktop))
253 self:clearln(1, table.unpack(theme.titleBar))
254 self:writeXY(2, 1, app.name)
255 self:rightXY(self.width - 1, 1, "v"..app.version)
256 end
257 end
258 function this:init(width, height, depth)
259 self:cls("white", "black")
260 self._so.setResolution(width, height)
261 if (type(depth) == "number") then
262 if (depth == 1 or depth == 4 or depth == 8) then
263 self._so.setDepth(depth)
264 end
265 end
266 self.width, self.height = self._so.getResolution()
267 self.depth = self._so.getDepth()
268 end
269 function this:close()
270 self:cls("white", "black")
271 self:drawRect(1, 1, self._ow, self._oh, " ")
272 self._so.setResolution(self._ow, self._oh)
273 self._so.setDepth(self._od)
274 self:cls("white", "black")
275 if (package.loaded.term) then
276 package.loaded.term.setCursor(1, 1)
277 else
278 require("term").setCursor(1, 1)
279 end
280 end
281
282 return this
283end
284local term = newTerm()
285
286-- Self contained list object
287-- Requires: term object !!
288local function newList(x, y, width, height)
289 local this = {}
290
291 this._x = 1
292 this._y = 1
293 this._w = 0
294 this._h = 0
295 this.width = 0
296 this.height = 0
297 this._nf = "white"
298 this._nb = "gray"
299 this._hf = "black"
300 this._hb = "orange"
301 this._le = {}
302 this._si = 1
303 this._pi = 0
304 this._lo = 0
305 this._ov = true
306 this._ur = true
307 this._urf = true
308
309 function this:clear(all)
310 self._le = nil
311 self._le = {}
312 if (all) then
313 self._x = 1
314 self._y = 1
315 self._w = 0
316 self._h = 0
317 self.width = 0
318 self.height = 0
319 self._si = 1
320 self._lo = 0
321 self._ur = true
322 self._urf = true
323 end
324 os.sleep(0)
325 end
326 function this:setXY(x, y)
327 if (tonumber(x) and tonumber(y)) then
328 if (self._x ~= x or self._y ~= y) then
329 self._x = x
330 self._y = y
331 self._ur = true
332 self._urf = true
333 end
334 end
335 end
336 function this:getXY()
337 return self._x, self._y
338 end
339 function this:setWidth(width)
340 if (tonumber(width)) then
341 if (self._w ~= width) then
342 self._w, self.width = width, width
343 self._ur = true
344 self._urf = true
345 end
346 end
347 end
348 function this:setHeight(height)
349 if (tonumber(height)) then
350 if (self._h ~= height) then
351 self._h, self.height = height, height
352 self._ur = true
353 self._urf = true
354 end
355 end
356 end
357 function this:setSize(width, height)
358 if (tonumber(width) and tonumber(height)) then
359 if (self._w ~= width or self._h ~= height) then
360 self._w, self.width = width, width
361 self._h, self.height = height, height
362 self._ur = true
363 self._urf = true
364 end
365 end
366 end
367 function this:getSize()
368 return self._w, self._h
369 end
370 function this:setNormal(fore, back)
371 if (type(fore) == "string" and type(back) == "string") then
372 self._nf, self._nb = fore, back
373 end
374 end
375 function this:setHighlight(fore, back)
376 if (type(fore) == "string" and type(back) == "string") then
377 self._hf, self._hb = fore, back
378 end
379 end
380 function this:add(entry)
381 if (entry) then
382 table.insert(self._le, tostring(entry))
383 self._ur, self._urf = true, true
384 end
385 end
386 function this:setList(list)
387 if (type(list) == "table") then
388 self._le = nil
389 self._le = {}
390 for l = 1, #list do
391 table.insert(self._le, tostring(list[l]))
392 end
393 os.sleep(0)
394 self._ur, self._urf = true, true
395 end
396 end
397 function this:getList()
398 return self._le
399 end
400 function this:getSelectedIndex()
401 if (#self._le > 0) then
402 return self._si
403 else
404 return 0
405 end
406 end
407 function this:getSelectedEntry()
408 if (#self._le > 0) then
409 return self._le[self._si]
410 else
411 return ""
412 end
413 end
414 function this:refresh()
415 self._ur = true
416 self._urf = true
417 return true
418 end
419 function this:render()
420 if (self._ur) then
421 local spc = true
422 for y = 1, self._h do
423 local i = self._lo + y
424 if (i <= #self._le) then
425 if (i == self._si) then
426 term:color(self._hf, self._hb)
427 spc = true
428 else
429 if ((i == self._pi or self._urf) and spc) then
430 term:color(self._nf, self._nb)
431 spc = false
432 end
433 end
434 if ((i == self._pi or self._urf) or i == self._si) then
435 local le = " "..self._le[i]..string.rep(" ", self._w)
436 if (#le > self._w) then
437 le = string.sub(le, 1, self._w)
438 end
439 term:writeXY(self._x, self._y + (y - 1), le)
440 le = nil
441 end
442 elseif (self._urf) then
443 if (spc) then
444 term:color(self._nf, self._nb)
445 end
446 term:writeXY(self._x, self._y + (y - 1), string.rep(" ", self._w))
447 end
448 end
449 spc = nil
450 self._urf = false
451 self._ur = false
452 os.sleep(0)
453 end
454 end
455 function this:_mud(key)
456 if (key == 200) then
457 if (self._si > 1) then
458 self._pi = self._si
459 self._si = self._si - 1
460 if (self._si < self._lo + 1) then
461 self._lo = self._lo - 1
462 self._urf = true
463 end
464 self._ur = true
465 end
466 elseif (key == 208) then
467 if (self._si + 1 <= #self._le) then
468 self._pi = self._si
469 self._si = self._si + 1
470 if (self._si - self._lo > self._h) then
471 self._lo = self._lo + 1
472 self._urf = true
473 end
474 self._ur = true
475 end
476 end
477 end
478 function this:checkEvent(...)
479 local eventID, arg1, arg2, arg3, arg4, arg5 = table.unpack({...})
480 if (eventID == "key_down" and #self._le > 0) then
481 local key = tonumber(arg3)
482 if (key == 199) then -- Home
483 if (self._si > 1) then
484 self._pi = self._si
485 self._si = 1
486 if (self._lo > 0) then
487 self._lo = 0
488 self._urf = true
489 end
490 self._ur = true
491 end
492 return nil
493 elseif (key == 207) then -- End
494 if (self._si < #self._le) then
495 self._si = #self._le
496 self._pi = 0
497 self._lo = #self._le - self._h
498 if (self._lo < 0) then
499 self._lo = 0
500 end
501 self._ur = true
502 self._urf = true
503 end
504 return nil
505 elseif (key == 201) then -- PgUp
506 if (self._si > self._lo + 1) then
507 self._pi = self._si
508 self._si = self._lo + 1
509 self._ur = true
510 elseif (self._si == self._lo + 1) then
511 if (self._lo > 0) then
512 self._lo = self._lo - self._h
513 if (self._lo < 0) then
514 self._lo = 0
515 end
516 self._si = self._lo + 1
517 self._ur = true
518 self._urf = true
519 end
520 end
521 return nil
522 elseif (key == 209) then -- PgDn
523 if (self._si < self._lo + self._h) then
524 self._pi = self._si
525 self._si = self._lo + self._h
526 if (self._si > #self._le) then
527 self._si = #self._le
528 end
529 self._ur = true
530 else
531 if (self._si < #self._le) then
532 self._lo = self._lo + self._h
533 if (self._lo > #self._le - self._h) then
534 self._lo = #self._le - self._h
535 end
536 self._si = self._lo + self._h
537 if (self._si > #self._le) then
538 self._si = #self._le
539 end
540 self._pi = 0
541 self._ur = true
542 self._urf = true
543 end
544 end
545 return nil
546 elseif (key == 200) then -- Up
547 self:_mud(key)
548 return nil
549 elseif (key == 208) then -- Down
550 self:_mud(key)
551 return nil
552 elseif (key == 28) then -- Enter
553 return "listbox_select", arg1, self._si, self._le[self._si], arg4, nil
554 end
555 elseif (eventID == "touch" and #self._le > 0) then
556 local x, y = tonumber(arg2), tonumber(arg3)
557 if ((x >= self._x and x <= (self._x + self._w) - 1) and (y >= self._y and y <= (self._y + self._h) - 1)) then
558 if (arg4 == 0) then
559 local ni = self._lo + ((y - self._y) + 1)
560 if (ni == self._si) then
561 return "listbox_select", arg1, self._si, self._le[self._si], arg5, nil
562 elseif (ni <= #self._le) then
563 self._pi = self._si
564 self._si = ni
565 self._ur = true
566 return nil
567 end
568 end
569 end
570 elseif (eventID == "scroll" and #self._le > 0) then
571 local x, y = tonumber(arg2), tonumber(arg3)
572 if ((x >= self._x and x <= (self._x + self._w) - 1) and (y >= self._y and y <= (self._y + self._h) - 1)) then
573 if (arg4 == 1) then
574 self:_mud(200)
575 return nil
576 elseif (arg4 == -1) then
577 self:_mud(208)
578 return nil
579 end
580 end
581 end
582 return eventID, arg1, arg2, arg3, arg4, arg5
583 end
584 if (type(x) == "number" and type(y) == "number") then
585 this:setXY(x, y)
586 end
587 if (type(width) == "number" and type(height) == "number") then
588 this:setSize(width, height)
589 end
590
591 return this
592end
593
594-- ## StatusBar caption functions
595-- Splits an ampersand highlighted caption into a key/label pair
596local function fixCaption(caption)
597 local key, keyPos, label, work = 0, 0, "", ""
598 work = string.gsub(caption, "&&", ";amp;")
599 local amp = string.find(work, "&")
600 if (amp) then
601 key = string.sub(work, amp + 1, amp + 1)
602 keyPos = tonumber(amp)
603 label = string.gsub(string.gsub(work, "&", ""), ";amp;", "&")
604 return true, key, keyPos, label
605 end
606 return false, 0, 0, string.gsub(work, ";amp;", "&")
607end
608
609-- Alternative key names for on-screen display
610local function getKeyName(keyNum)
611 local tbl = {
612 [59] = "F1",
613 [60] = "F2",
614 [61] = "F3",
615 [62] = "F4",
616 [63] = "F5",
617 [64] = "F6",
618 [65] = "F7",
619 [66] = "F8",
620 [67] = "F9",
621 [68] = "F10",
622 [87] = "F11",
623 [88] = "F12",
624 [183] = "PrnScr",
625 [70] = "ScrLck",
626 [197] = "Pause",
627 [41] = "Tilde",
628 [12] = "Minus",
629 [13] = "Equals",
630 [14] = "BckSpc",
631 [210] = "Ins",
632 [199] = "Home",
633 [201] = "PgUp",
634 [69] = "NumLck",
635 [181] = "Pad/",
636 [55] = "Pad*",
637 [74] = "Pad-",
638 [15] = "Tab",
639 [26] = "LBracket",
640 [27] = "RBracket",
641 [43] = "BSlash",
642 [211] = "Del",
643 [207] = "End",
644 [209] = "PgDn",
645 [71] = "Pad7",
646 [72] = "Pad8",
647 [73] = "Pad9",
648 [78] = "Pad+",
649 [58] = "CpsLck",
650 [39] = "SColon",
651 [40] = "Apos",
652 [28] = "Enter",
653 [75] = "Pad4",
654 [76] = "Pad5",
655 [77] = "Pad6",
656 [42] = "LShift",
657 [51] = "Comma",
658 [52] = "Period",
659 [53] = "Slash",
660 [54] = "RShift",
661 [200] = "Up",
662 [79] = "Pad1",
663 [80] = "Pad2",
664 [81] = "Pad3",
665 [29] = "LCtrl",
666 [219] = "LSuper",
667 [56] = "Alt",
668 [57] = "Space",
669 [184] = "AltGr",
670 [220] = "RSuper",
671 [221] = "WMenu",
672 [157] = "RShift",
673 [203] = "Left",
674 [208] = "Down",
675 [205] = "Right",
676 [82] = "Pad0",
677 [83] = "Pad."
678 }
679 local res = tbl[keyNum] or "???"
680 tbl = nil
681 os.sleep(0)
682 return res
683end
684
685-- Snippet: statusBar
686local function newStatusBar(...)
687 local this = {}
688
689 this._nt = ""
690 this._ht = {}
691
692 function this:set(...)
693 local labels = {...}
694 self._nt = ""
695 self._ht = nil
696 self._ht = {}
697 local x, keyCap = 2, ""
698 for l = 1, #labels do
699 if (type(labels[l]) == "number") then
700 keyCap = getKeyName(labels[l])
701 elseif (type(labels[l]) == "string") then
702 local success, key, keyPos, cap = fixCaption(labels[l])
703 if (not success) then
704 key = 0
705 keyPos = 0
706 end
707 if (#keyCap > 0) then
708 if (term.depth == 1) then
709 self._nt = self._nt.."["..keyCap.."]: "..cap
710 else
711 self._nt = self._nt.."["..string.rep(" ", #keyCap).."]: "..cap
712 table.insert(self._ht, { (x + 1), keyCap } )
713 end
714 else
715 if (keyPos > 0) then
716 if (term.depth == 1) then
717 self._nt = self._nt.."["..string.upper(key).."]: "..cap
718 else
719 self._nt = self._nt..cap
720 table.insert(self._ht, { (x + (keyPos - 1)), key } )
721 end
722 else
723 self._nt = self._nt..cap
724 end
725 end
726 end
727 if (type(labels[l]) == "string" and l < #labels) then
728 self._nt = self._nt..", "
729 end
730 x = #self._nt + 2
731 end -- for l
732 os.sleep(0)
733 end
734 function this:render()
735 if (self._nt ~= "") then
736 term:clearln(term.height, table.unpack(theme.desktop))
737 term:writeXY(2, term.height, self._nt)
738 if (#self._ht > 0) then
739 term:color(table.unpack(theme.highlight))
740 for h = 1, #self._ht do
741 term:writeXY(self._ht[h][1], term.height, self._ht[h][2])
742 end
743 end
744 end
745 end
746 this:set(...)
747
748 return this
749end
750-- ### End of statusBar caption functions
751
752-- Text padding; both left and right
753local function pad(text, size, pre)
754 local result = tostring(text)
755 while #result < size do
756 if (pre) then
757 result = " "..result
758 else
759 result = result.." "
760 end
761 end
762 return result
763end
764
765-- Splitter function
766local function split(str, pat)
767 local r = {}
768 for s in string.gmatch(str, "[^"..pat.."]+") do
769 table.insert(r, s)
770 end
771 return r
772end
773-- Wrap text inside specified width
774local function wrapText(text, width)
775 local txt = split(text, "\n")
776 local l = {}
777 for t = 1, #txt do
778 local cl = ""
779 local msg = ""..txt[t]..""
780 for w in msg:gmatch("%S+%s*") do
781 if (#cl + #w >= width) then
782 table.insert(l, cl)
783 cl = w
784 else
785 cl = cl..w
786 end
787 end -- for
788 if (cl ~= "") then
789 table.insert(l, cl)
790 end
791 end
792 return l
793end
794
795-- Remove preceeding & trailing spaces
796local function trim(text)
797 local txt = tostring(text)
798 if (string.sub(txt, 1, 1) == " ") then
799 while (#txt > 0 and string.sub(txt, 1, 1) == " ") do
800 txt = string.sub(txt, 2, #txt)
801 end
802 end
803 if (string.sub(txt, #txt, #txt) == " ") then
804 while (#txt > 0 and string.sub(txt, #txt, #txt) == " ") do
805 txt = string.sub(txt, 1, #txt - 1)
806 end
807 end
808 return txt
809end
810
811-- Some doc's return "int" instead of "number"
812local function fixVars(v)
813 if (string.find(v, ",")) then
814 local lst = split(v, ",")
815 for l = 1, #lst do
816 if (string.lower(lst[l]) == "int") then
817 lst[l] = "number"
818 end
819 end
820 return table.concat(lst, ",")
821 else
822 if (string.lower(v) == "int") then
823 return "number"
824 end
825 end
826 return v
827end
828
829-- Splits function parameters into browsable table structures
830local function splitParameters(params)
831 if (params ~= "" and params ~= nil) then
832 local aopt = false
833 if (string.sub(params, 1, 1) == "[" and string.sub(params, #params, #params) == "]") then
834 params = string.sub(params, 2, #params)
835 params = string.sub(params, 1, #params - 1)
836 aopt = true
837 end
838 local result = {}
839 local args = split(params, ",")
840 local an, at, aat, tn, vn, vt = "", "", "", ""
841 local opt, itl, ivl, vo, sr = false, false, false, false, false
842 local tl, vl = {}, {}
843 for a = 1, #args do
844 sr = false;vo = false
845 if (string.find(args[a], "[:]")) then
846 local ap = split(args[a], ":")
847 an = ap[1];at = ap[2];aat = ap[3] or ""
848 else
849 an = args[a];at = "";aat = ""
850 end
851 an = string.gsub(an, " ", "");at = string.gsub(at, " ", "");aat = string.gsub(aat, " ", "")
852 if (string.sub(at, #at, #at) == "[") then
853 at = string.gsub(at, "[[]", "")
854 if (string.find(at, "[?]")) then
855 at = string.gsub(at, "[?]", "");vo = true;sr = true
856 else
857 sr = true
858 end
859 opt = true
860 elseif (string.sub(an, 1, 1) == "[") then
861 an = string.sub(an, 2, #an)
862 sr = true
863 opt = true
864 elseif (string.sub(at, #at, #at) == "]" and opt) then
865 opt = false;at = string.gsub(at, "[]]", "");vo = true;sr = true
866 elseif (string.sub(at, 1, 1) == "{") then
867 tn = an;tl = {};itl = true;at = string.gsub(at, "[{]", "")
868 if (string.find(aat, "[?]")) then
869 aat = string.gsub(aat, "[?]", "")
870 table.insert(tl, { at, aat, true } )
871 else
872 table.insert(tl, { at, aat, false } )
873 end
874 elseif (string.find(at, "[{]")) then
875 local tp = split(at, "{")
876 if (tp[1] == "table") then
877 tn = an;tl = {};itl = true
878 if (string.find(aat, "[?]")) then
879 aat = string.gsub(aat, "[?]", "")
880 table.insert(tl, { tp[2], aat, true } )
881 else
882 table.insert(tl, { tp[2], aat, false } )
883 end
884 else
885 vn = an;vt = tp[1];vl = {};ivl = true
886 table.insert(vl, tp[2])
887 end
888 elseif (string.find(an, "[}]")) then
889 local tp = split(an, "}")
890 if (itl) then
891 table.insert(tl, { tp[1], "", false } )
892 if (tp[2] == "?") then
893 vo = true
894 end
895 sr = true;an = tn;at = "table"
896 elseif (ivl) then
897 table.insert(vl, tp[1])
898 if (tp[2] == "?") then
899 vo = true
900 end
901 sr = true;an = vn;at = vt
902 end
903 elseif (itl) then
904 if (string.find(at, "[?]")) then
905 at = string.gsub(at, "[?]", "")
906 table.insert(tl, { an, at, true } )
907 else
908 table.insert(tl, { an, at, false } )
909 end
910 elseif (ivl) then
911 table.insert(vl, an)
912 else
913 if (string.find(at, "[?]")) then
914 at = string.gsub(at, "[?]", "");vo = true;sr = true
915 elseif (opt) then
916 vo = true;sr = true
917 else
918 sr = true
919 end
920 end
921 if (sr) then
922 if (aopt) then
923 vo = true
924 end
925 if (itl) then
926 table.insert(result, { an, fixVars(at), vo, tl } )
927 itl = false;tl = {};tn = ""
928 elseif (ivl) then
929 table.insert(result, { an, fixVars(at), vo, vl } )
930 ivl = false;vl = {};vn = "";vt = ""
931 else
932 table.insert(result, { an, fixVars(at), vo, false } )
933 end
934 end
935 end
936 return result
937 end
938 return {}
939end
940
941-- Splits returned documentation into browsable table structures
942local function splitDoc(methodName, methodType, methodDoc)
943 if (methodDoc) then
944 local code, doc, mn, mt, rt, params = "", "", methodName, "", "", ""
945 local ma = {}
946 local rem1, rem2 = string.find(methodDoc, "[--]")
947 if (rem1 and rem2) then
948 code = string.sub(methodDoc, 1, rem1 - 2)
949 doc = string.sub(methodDoc, rem2 + 3, #methodDoc)
950 end
951 if (code ~= "") then
952 local ps1, ps2 = string.find(code, "[(]")
953 local pe1, pe2 = string.find(code, "[)]")
954 if (ps1 and pe1) then
955 mt = string.sub(code, 1, ps1 - 1)
956 params = string.sub(code, ps1 + 1, pe1 - 1)
957 local tmp = string.sub(code, pe1 + 1, #code)
958 local pc1, pc2 = string.find(tmp, "[:]")
959 if (pc1) then
960 rt = fixVars(trim(string.sub(tmp, pc1 + 1, #tmp)))
961 end
962 end
963 end
964 return { mn, mt, splitParameters(params), rt, doc }
965 else
966 if (methodName == "address" and methodType == "string") then
967 return { methodName, methodType, false, false, "The address of this component." }
968 elseif (methodName == "type" and methodType == "string") then
969 return { methodName, methodType, false, false, "Which type this component is." }
970 elseif (methodName == "slot" and methodType == "number") then
971 return { methodName, methodType, false, false, "The slot this component is installed in." }
972 else
973 return { methodName, methodType, false, false, "No further documentation available." }
974 end
975 end
976 return nil
977end
978
979local function findAddress(partial, addressList)
980 if (string.find(partial, "[..]") or string.find(partial, " ")) then
981 if (string.find(partial, "[..]")) then
982 partial = string.sub(partial, 1, string.find(partial, "[..]") - 1)
983 end
984 if (string.find(partial, " ")) then
985 partial = string.sub(partial, 1, string.find(partial, " ") - 1)
986 end
987 end
988 for a = 1, #addressList do
989 if (string.sub(addressList[a], 1, #partial) == partial) then
990 return addressList[a]
991 end
992 end
993 return partial
994end
995
996-- Get a table of filesystem mounts:
997-- address, label, mode, path
998local function getMounts(address)
999 local result = {}
1000 for proxy, path in fs.mounts() do
1001 local label = proxy.getLabel() or ""
1002 local mode = proxy.isReadOnly() and "ro" or "rw"
1003 if (address) then
1004 if (proxy.address == address) then
1005 table.insert(result, { proxy.address, label, mode, path } )
1006 end
1007 else
1008 table.insert(result, { proxy.address, label, mode, path } )
1009 end
1010 end
1011 return result
1012end
1013
1014-- Get a list of all attached/visible components
1015-- sorted by type, sub-sorted by address
1016local function getComponents()
1017 local result = {}
1018 local cal, ctl = {}, {}
1019 for a, t in components.list() do
1020 local found = false
1021 for c = 1, #ctl do
1022 if (ctl[c] == t) then
1023 found = true
1024 break
1025 end
1026 end
1027 if (not found) then
1028 table.insert(ctl, t)
1029 end
1030 if (not cal[t]) then
1031 cal[t] = {}
1032 end
1033 table.insert(cal[t], a)
1034 end
1035 table.sort(ctl)
1036 for c = 1, #ctl do
1037 local al = {}
1038 for a = 1, #cal[ctl[c]] do
1039 table.insert(al, cal[ctl[c]][a])
1040 end
1041 table.sort(al)
1042 table.insert(result, { ctl[c], al } )
1043 al = nil
1044 end
1045 ctl = nil
1046 cal = nil
1047 return result
1048end
1049
1050local function showHelp()
1051 local statusBar = newStatusBar("&Quit", 14, "Close help screen")
1052 term:cleartb()
1053 statusBar:render()
1054 term:color(table.unpack(theme.desktop))
1055 term:writeXY(2, 3, "The following keys can be used to navigate")
1056 term:writeXY(2, 4, "and select on most screens.")
1057 term:writeXY(2, 6, "[ArrowKeys]: Move selection.")
1058 term:writeXY(2, 7, "[Enter] : Select highlighted entry.")
1059 term:writeXY(2, 8, "[BckSpc] : Back to previous menu.")
1060 term:writeXY(2, 10, "[M] : Display mount information.")
1061 term:writeXY(15, 12, "Only on address screen.")
1062 term:writeXY(2, 14, "[Q] : Quit and return to OS.")
1063 term:color(table.unpack(theme.desktopError))
1064 term:writeXY(15, 11, "FileSystems only!")
1065 local running = true
1066 local res
1067 while running do
1068 local e, p1, p2, p3, p4, p5 = event.pull()
1069 if ((e == "key_down" and p3 == 14) or (e == "touch" and p4 == 1)) then
1070 running = nil
1071 elseif (e == "key_down" and p3 == 16) then
1072 res = "quit"
1073 running = nil
1074 end
1075 end
1076 statusBar = nil
1077 return res
1078end
1079
1080-- Check all the standard events
1081local function stdEvents(e, p1, p2, p3, p4, p5, ct)
1082 if (e) then
1083 if (e == "key_down") then
1084 if (p3 == 14) then
1085 return "menu_back"
1086 else
1087 local c = string.lower(uni.char(p2))
1088 if (p3 == 50 and ct == "filesystem") then -- "m"
1089 return "menu_mount"
1090 elseif (p3 == 35) then -- "h"
1091 if (showHelp() == "quit") then
1092 return "quit"
1093 else
1094 return "ui_refresh"
1095 end
1096 elseif (p3 == 16) then -- "q"
1097 return "quit"
1098 end
1099 end
1100 elseif (e == "touch" and p4 == 1) then
1101 return "menu_back"
1102 elseif (string.sub(e, 1, 10) == "component_") then
1103 -- Force main menu to refresh component list
1104 refreshComponents = true
1105 end
1106 end
1107 return nil
1108end
1109
1110local function varName(componentType)
1111 local ren = {
1112 chest = {
1113 "copper", "crystal", "diamond", "dirtchest9000", "gold", "iron", "obsidian", "silver", "blockvacuumchest",
1114 "extrautils_filing_cabinet",
1115 },
1116 machine = {
1117 "tile_blockbuffer_item", "tileentityfluidcrafter", "blockcrafter", "tileentityhardmedrive", "tilechest",
1118 "tiledrive", "tileentityfluidfiller", "tileentityvibrationchamberfluid", "alloy_smelter", "blocksagmill",
1119 "auto_painter", "blockvat", "blockwirelesschargertileentity", "farming_station", "blocktransceiver",
1120 "blockpoweredspawner", "blocksliceandsplice", "blocksoulbinder", "blocktelepadtileentity",
1121 "blockattractor", "blockenchanter", "blockexperienceobelisk", "blockinhibitorobelisk", "blockkillerjoe",
1122 "blockspawnguard", "blockweatherobelisk",
1123 },
1124 loader = { "adv__item__loader", "fluid_loader", "item_loader", },
1125 unloader = { "adv__item__unloader", "fluid_unloader", "item_unloader", },
1126 dispenser = { "cart_dispenser", "train_dispenser", },
1127 export = { "me_exportbus", },
1128 import = { "me_importbus", },
1129 interface = { "me_interface", },
1130 tank = { "tileentitycertustank", "blockreservoirtileentity", },
1131 generator = { "blockcombustiongenerator", "blockzombiegenerator", "stirling_generator", "generatorfurnace", },
1132 solar = { "blocksolarpaneltileentity", "extrautils_generatorsolar", },
1133 power = { "tile_blockcapacitorbank_name", },
1134 monitor = { "tile_blockpowermonitor", "blockinventorypanel", },
1135 barrel = { "mcp_mobius_betterbarrel", },
1136 trashcan = { "tileentitytrashcanenergy", "tileentitytrashcanfluids", },
1137 }
1138 local repl = {
1139 "openperipheral_", "tile_thermalexpansion_device_", "tile_thermalexpansion_dynamo_",
1140 "tile_thermalexpansion_machine_", "tile_thermalexpansion_", "thermalexpansion_",
1141 }
1142 local repl2 = {
1143 "_basic_name", "_name",
1144 }
1145 local res = string.lower(componentType)
1146 for r = 1, #repl do
1147 local tmp = string.sub(res, 1, #repl[r])
1148 if (string.sub(res, 1, #repl[r]) == repl[r]) then
1149 res = string.sub(res, #tmp + 1, #res)
1150 for i = 1, #repl2 do
1151 tmp = string.sub(res, (#res - #repl2[i]) + 1, #res)
1152 if (tmp == repl2[i]) then
1153 res = string.sub(res, 1, (#res - #repl2[i]))
1154 break
1155 end
1156 end
1157 return res
1158 end
1159 end
1160 -- Check for renaming
1161 for nr, lst in pairs(ren) do
1162 for l = 1, #lst do
1163 if (res == lst[l]) then
1164 return nr
1165 end
1166 end
1167 end
1168 return componentType
1169end
1170
1171local function shiftMore(show)
1172 term:color(table.unpack(theme.desktop))
1173 if (show) then
1174 term:rightXY(term.width + 3, term.height, "[ ]: "..uni.char(0x25bc).." view more "..uni.char(0x25bc))
1175 term:color(table.unpack(theme.highlight))
1176 term:rightXY(term.width - 17, term.height, "Shift")
1177 else
1178 term:rightXY(term.width, term.height, string.rep(" ", 23))
1179 end
1180end
1181
1182local function clua(ct)
1183 term:color(table.unpack(theme.lua[ct]))
1184end
1185
1186-- Menu: Method Details
1187local function menuDoc(componentType, doc)
1188 local objName = varName(componentType)
1189 if (#objName > 10) then
1190 objName = "peripheral"
1191 end
1192 local mn, mt = " "..doc[1].." ", doc[2]
1193 local x, y, methodx, methody, docY, docD = 1, 1, 0, 0, 0, 0
1194 local hit = {}
1195 local docLines = wrapText(doc[5], term.width - 2)
1196 local running = true
1197 local refreshUI, uiFull, ms, sh, cb = true, true, true, false, false
1198 local pi, opi = 1, 0
1199 local res
1200
1201 local statusBar = newStatusBar(14, "Back")
1202 while running do
1203 if (refreshUI) then
1204 if (uiFull) then
1205 hit = {}
1206 term:cleartb()
1207 statusBar:render()
1208
1209 term:color(table.unpack(theme.desktopGray))
1210 term:writeXY(2, 3, componentType)
1211
1212 if (doc[3]) then
1213 local rts = { "nil" }
1214 if (doc[4]) then
1215 if (string.find(doc[4], " or ")) then
1216 rts = split(string.gsub(doc[4], " or ", "----"), "----")
1217 else
1218 rts = { doc[4] }
1219 end
1220 end
1221 if (rts[1] == "") then
1222 rts[1] = "nil"
1223 end
1224 if (rts[1] == "nil") then
1225 clua("consts")
1226 else
1227 clua("text")
1228 end
1229 term:writeXY(2, 5, rts[1])
1230 clua("operators")
1231 term:write(" = ")
1232 clua("functs")
1233 term:writeXY(4, 6, "function")
1234 clua("text")
1235 methodx, methody = term:getXY()
1236 term:write(mn)
1237 clua("delims")
1238 if (#doc[3] > 0) then
1239 term:write("(")
1240 x, y = 5, 7
1241 term:gotoXY(x, y)
1242 for p = 1, #doc[3] do
1243 x, y = term:getXY()
1244 if (x + #doc[3][p][1] + 3 > term.width) then
1245 x = 5
1246 y = y + 1
1247 term:gotoXY(x, y)
1248 end
1249 if (doc[3][p][3]) then
1250 clua("optionals")
1251 else
1252 clua("text")
1253 end
1254 local arg = " "..doc[3][p][1].." "
1255 table.insert(hit, { x, y, arg } )
1256 term:write(arg)
1257 if (p < #doc[3]) then
1258 clua("delims")
1259 term:write(",")
1260 end
1261 end
1262 y = y + 1
1263 clua("delims")
1264 term:writeXY(4, y, ")")
1265 else
1266 term:write("()")
1267 end
1268 x, y = term:getXY()
1269 docY = y + 2
1270 else
1271 clua("text")
1272 term:writeXY(2, 5, mt)
1273 clua("operators")
1274 term:write(" = ")
1275 clua("optionals")
1276 term:write(objName)
1277 clua("operators")
1278 term:write(".")
1279 clua("text")
1280 term:write(trim(mn))
1281 x, y = term:getXY()
1282 docY = y + 2
1283 end
1284 uiFull = false
1285 end -- uiFull
1286 docD = term.height - docY
1287 if (opi > 0) then
1288 if (doc[3][opi][3]) then
1289 clua("optionals")
1290 else
1291 clua("text")
1292 end
1293 term:writeXY(hit[opi][1], hit[opi][2], hit[opi][3])
1294 opi = 0
1295 end
1296 if (ms) then
1297 -- Method mode
1298 if (type(doc[3]) == "table") then
1299 if (#doc[3] > 0) then
1300 term:color(table.unpack(theme.menu.highlight))
1301 term:writeXY(methodx, methody, mn)
1302 end
1303 end
1304 term:color(table.unpack(theme.desktop))
1305 term:drawRect(1, docY, term.width, term.height - docY, " ")
1306 if (#docLines > docD) then
1307 if (keyb.isShiftDown()) then
1308 term:drawRect(1, 3, term.width, term.height - 3, " ")
1309 for d = 1, #docLines do
1310 if (d + 2 < term.height) then
1311 term:writeXY(2, d + 2, docLines[d])
1312 end
1313 end
1314 shiftMore(false)
1315 else
1316 for d = 1, docD do
1317 term:writeXY(2, (docY + d) - 1, docLines[d])
1318 end
1319 shiftMore(true)
1320 end
1321 else
1322 for d = 1, #docLines do
1323 term:writeXY(2, (docY + d) - 1, docLines[d])
1324 end
1325 end
1326 elseif (doc[3] and pi > 0) then
1327 -- Parameter mode
1328 term:color(table.unpack(theme.menu.highlight))
1329 term:writeXY(hit[pi][1], hit[pi][2], hit[pi][3])
1330
1331 term:color(table.unpack(theme.desktop))
1332 term:drawRect(1, docY, term.width, term.height - docY, " ")
1333 if (doc[3][pi][3]) then
1334 clua("optionals")
1335 term:writeXY(2, docY, "Parameter "..pi.." (optional):")
1336 else
1337 clua("text")
1338 term:writeXY(2, docY, "Parameter "..pi..":")
1339 end
1340 term:writeXY(2, docY + 2, doc[3][pi][1])
1341 clua("operators")
1342 term:write(" : ")
1343 if (doc[3][pi][3]) then
1344 clua("optionals")
1345 else
1346 clua("text")
1347 end
1348 term:write(doc[3][pi][2])
1349 if (doc[3][pi][4]) then
1350 if (doc[3][pi][2] == "table") then
1351 local mtn, mtt = 0, 0
1352 local startY = docY + 1
1353 for t = 1, #doc[3][pi][4] do
1354 if (#doc[3][pi][4][t][1] > mtn) then
1355 mtn = #doc[3][pi][4][t][1]
1356 end
1357 if (#doc[3][pi][4][t][2] > mtt) then
1358 mtt = #doc[3][pi][4][t][2]
1359 end
1360 end
1361 local col1 = 50 - (mtn + mtt + 4)
1362 local sep = col1 + mtn + 1
1363 local col2 = sep + 2
1364 -- Render the contents...
1365 clua("text")
1366 term:writeXY(col1, startY - 1, doc[3][pi][1])
1367 clua("operators")
1368 term:write(" = ")
1369 clua("delims")
1370 term:write("{")
1371 for t = 1, #doc[3][pi][4] do
1372 if (doc[3][pi][4][t][3]) then
1373 clua("optionals")
1374 term:writeXY(col1 - 1, (startY + t) - 1, "("..doc[3][pi][4][t][1])
1375 else
1376 clua("text")
1377 term:writeXY(col1, (startY + t) - 1, doc[3][pi][4][t][1])
1378 end
1379 if (doc[3][pi][4][t][2] ~= "") then
1380 clua("operators")
1381 term:writeXY(sep, (startY + t) - 1, ":")
1382 if (doc[3][pi][4][t][3]) then
1383 clua("optionals")
1384 term:writeXY(col2, (startY + t) - 1, doc[3][pi][4][t][2]..")")
1385 else
1386 clua("text")
1387 term:writeXY(col2, (startY + t) - 1, doc[3][pi][4][t][2])
1388 end
1389 else
1390 if (doc[3][pi][4][t][3]) then
1391 term:write(")")
1392 end
1393 end
1394 end
1395 clua("delims")
1396 term:rightXY(col1, startY + #doc[3][pi][4], "}")
1397 else
1398 term:color(table.unpack(theme.desktop))
1399 term:writeXY(2, docY + 4, "Values: ")
1400 for v = 1, #doc[3][pi][4] do
1401 local txt = doc[3][pi][4][v]
1402 local vx, vy = term:getXY()
1403 if (vx + #txt + 2 > term.width) then
1404 term:gotoXY(10, vy + 1)
1405 end
1406 term:write(txt)
1407 if (v < #doc[3][pi][4]) then
1408 term:write(", ")
1409 end
1410 end
1411 end
1412 end
1413 end
1414
1415 refreshUI = false
1416 end
1417
1418 local e, p1, p2, p3, p4, p5 = event.pull()
1419 local r = stdEvents(e, p1, p2, p3, p4, p5)
1420 if (r == "menu_back") then
1421 running = nil
1422 elseif (r == "ui_refresh") then
1423 uiFull = true
1424 refreshUI = true
1425 elseif (r == "quit") then
1426 res = "quit"
1427 running = nil
1428 elseif (e) then
1429 if (e == "key_down") then
1430 if (ms) then
1431 if (#docLines > docD) then
1432 if (keyb.isShiftDown() and not sh) then
1433 cb = cur.en
1434 cur.en = false
1435 sh = true
1436 refreshUI = true
1437 elseif (not keyb.isShiftDown() and sh) then
1438 sh = false
1439 uiFull = true
1440 refreshUI = true
1441 cur.en = cb
1442 end
1443 end
1444 if (doc[3] and p3 == 208) then -- arrow down: switch to parameter mode
1445 if (#doc[3] > 0) then
1446 pi = 1
1447 ms = false
1448 clua("text")
1449 term:writeXY(methodx, methody, mn)
1450 if (#docLines > docD) then
1451 shiftMore(false)
1452 end
1453 refreshUI = true
1454 end
1455 end
1456 else -- if ms
1457 if (doc[3] and p3 == 200) then -- arrow up: switch to method mode
1458 opi = pi
1459 pi = 1
1460 ms = true
1461 if (#docLines > docD) then
1462 shiftMore(true)
1463 end
1464 refreshUI = true
1465 elseif (doc[3] and p3 == 203) then
1466 -- previous parameter
1467 if (pi > 1) then
1468 opi = pi
1469 pi = pi - 1
1470 refreshUI = true
1471 end
1472 elseif (doc[3] and p3 == 205) then
1473 -- next parameter
1474 if (pi < #doc[3]) then
1475 opi = pi
1476 pi = pi + 1
1477 refreshUI = true
1478 end
1479 end
1480 end -- if ms
1481 elseif (e == "touch") then
1482 if (p4 == 0) then -- left
1483 local tx, ty = p2, p3
1484 if ((tx >= methodx and tx <= methodx + (#mn - 1)) and (ty == methody)) then
1485 if (not ms) then
1486 opi = pi
1487 pi = 1
1488 ms = true
1489 if (#docLines > docD) then
1490 shiftMore(true)
1491 end
1492 refreshUI = true
1493 end
1494 else
1495 -- Loop through any argument hits
1496 for h = 1, #hit do
1497 if ((tx >= hit[h][1] and tx <= hit[h][1] + (#hit[h][3] - 1)) and (ty == hit[h][2])) then
1498 if (ms) then
1499 pi = h
1500 ms = false
1501 clua("text")
1502 term:writeXY(methodx, methody, mn)
1503 if (#docLines > docD) then
1504 shiftMore(false)
1505 end
1506 refreshUI = true
1507 break
1508 else
1509 opi = pi
1510 pi = h
1511 refreshUI = true
1512 break
1513 end
1514 end
1515 end
1516 end
1517 end
1518 end
1519 end
1520 end
1521
1522 statusBar = nil
1523 os.sleep(0)
1524 return res
1525end
1526
1527-- Menu: Component Methods List
1528local function menuMethods(componentType, address)
1529 local running = true
1530 local refreshUI = true
1531 local docList = {}
1532 local statusBar = newStatusBar(14, "Back", 28, "View")
1533 local list = newList(1, 4, term.width, term.height - 5)
1534 local dev, reason = components.proxy(address)
1535 if (dev) then
1536 local lst = {}
1537 for n, v in pairs(dev) do
1538 table.insert(lst, n)
1539 end
1540 table.sort(lst)
1541 for l = 1, #lst do
1542 table.insert(docList, splitDoc(lst[l], type(dev[lst[l]]), components.doc(address, lst[l])))
1543 end
1544 lst = nil
1545 for d = 1, #docList do
1546 list:add(docList[d][1])
1547 end
1548 end
1549 local res
1550 while running do
1551 if (refreshUI) then
1552 term:cleartb()
1553 statusBar:render()
1554 if (dev) then
1555 term:color(table.unpack(theme.desktop))
1556 term:centerY(2, componentType)
1557 else
1558 term:color(table.unpack(theme.desktopError))
1559 term:centerY(3, "Component error!")
1560 term:color(table.unpack(theme.desktop))
1561 term:writeXY(2, 5, componentType)
1562 term:writeXY(2, 6, address)
1563 if (reason) then
1564 term:writeXY(2, 8, reason)
1565 else
1566 term:writeXY(2, 8, "Unable to connect to component")
1567 end
1568 end
1569 refreshUI = false
1570 end
1571 list:render()
1572 local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull())
1573 if (e == "listbox_select") then
1574 if (menuDoc(componentType, docList[p2]) == "quit") then
1575 res = "quit"
1576 running = nil
1577 else
1578 refreshUI = list:refresh()
1579 end
1580 elseif (e) then
1581 r = stdEvents(e, p1, p2, p3, p4, p5)
1582 if (r == "menu_back") then
1583 running = nil
1584 elseif (r == "ui_refresh") then
1585 refreshUI = list:refresh()
1586 elseif (r == "quit") then
1587 res = "quit"
1588 running = nil
1589 end
1590 end
1591 end
1592 dev = nil
1593 list:clear(true)
1594 list = nil
1595 statusBar = nil
1596 os.sleep(0)
1597 return res
1598end
1599
1600-- Menu: FileSystem Mounts
1601local function menuMounts(address)
1602 local running = true
1603 local refreshUI = true
1604 local mounts = getMounts(address)
1605 local statusBar = newStatusBar("&Quit", 14, "Back")
1606 while running do
1607 if (refreshUI) then
1608 term:cleartb()
1609 statusBar:render()
1610 term:color(table.unpack(theme.desktop))
1611 term:writeXY(2, 3, "Filesystem on:")
1612 term:writeXY(2, 4, address)
1613 local y = 6
1614 for m = 1, #mounts do
1615 term:color(table.unpack(theme.desktop))
1616 term:writeXY(2, y + 0, "Label:")
1617 term:writeXY(2, y + 1, "Mode :")
1618 term:writeXY(2, y + 2, "Path :")
1619 if (mounts[m][2] == "") then
1620 term:color(table.unpack(theme.desktopWarning))
1621 term:writeXY(9, y + 0, "{No label set}")
1622 else
1623 term:writeXY(9, y + 0, mounts[m][2])
1624 end
1625 if (mounts[m][3] == "ro") then
1626 term:color(table.unpack(theme.desktopError))
1627 term:writeXY(9, y + 1, "[ro] Read Only")
1628 else
1629 term:color(table.unpack(theme.desktopAccept))
1630 term:writeXY(9, y + 1, "[rw] Read Write")
1631 end
1632 term:color(table.unpack(theme.desktop))
1633 term:writeXY(9, y + 2, mounts[m][4])
1634 y = y + 4
1635 end
1636 refreshUI = false
1637 end
1638 local r = stdEvents(event.pull())
1639 if (r == "menu_back") then
1640 running = nil
1641 elseif (r == "ui_refresh") then
1642 refreshUI = true
1643 elseif (r == "quit") then
1644 return "quit"
1645 end
1646 end
1647 statusBar = nil
1648 os.sleep(0)
1649end
1650
1651-- Menu: Component address selection
1652local function menuAddress(componentType, addressList)
1653 local running = true
1654 local refreshUI = true
1655 local statusBar = nil
1656 if (componentType == "filesystem") then
1657 statusBar = newStatusBar("&Mount" , 14, "Back", 28, "View")
1658 else
1659 statusBar = newStatusBar(14, "Back", 28, "View")
1660 end
1661 local list = newList(1, 3, term.width, term.height - 4)
1662 if (componentType == "filesystem") then
1663 local mounts = getMounts()
1664 local address, label, mode, path
1665 local mwl, mwp = 0, 0
1666 for a = 1, #addressList do
1667 local count = 0
1668 for m = 1, #mounts do
1669 if (#mounts[m][2] > mwl) then
1670 mwl = #mounts[m][2]
1671 end
1672 if (#mounts[m][4] > mwp) then
1673 mwp = #mounts[m][4]
1674 end
1675 if (mounts[m][1] == addressList[a]) then
1676 address, label, mode, path = mounts[m][1], mounts[m][2], mounts[m][3], mounts[m][4]
1677 count = count + 1
1678 end
1679 end
1680 -- spacing + address + spacing + "[??] " + label + spacing + path + spacing
1681 if (term.width >= 1 + #addressList[1] + 1 + 5 + mwl + 1 + mwp + 1) then
1682 if (count > 1) then
1683 list:add(addressList[a].." "..pad(tostring(count), 4, true).." mounts")
1684 else
1685 list:add(addressList[a].." ["..mode.."] "..pad(label, mwl).." "..path)
1686 end
1687 else
1688 local diff = term.width - (mwl + mwp + 11)
1689 if (diff < 5) then
1690 diff = 5
1691 end
1692 if (count > 1) then
1693 list:add(string.sub(addressList[a], 1, diff)..".. "..pad(tostring(count), 4, true).." mounts")
1694 else
1695 list:add(string.sub(addressList[a], 1, diff)..".. ["..mode.."] "..pad(label, mwl).." "..path)
1696 end
1697 end
1698 end
1699 else
1700 local cap = componentType
1701 if (#cap > #addressList[1]) then
1702 local parts = split(cap, "_")
1703 local s = #parts
1704 cap = ""
1705 while #cap < #addressList[1] do
1706 cap = parts[1].."_..."
1707 for p = s, #parts do
1708 cap = cap.."_"..parts[p]
1709 end
1710 s = s - 1
1711 end
1712 s = s + 2
1713 cap = parts[1].."_..."
1714 for p = s, #parts do
1715 cap = cap.."_"..parts[p]
1716 end
1717 end
1718 for a = 1, #addressList do
1719 if (term.width > 1 + #addressList[1] + 1 + #cap + 1) then
1720 list:add(addressList[a].." "..cap)
1721 else
1722 local diff = term.width - (#cap + 5)
1723 if (diff < 5) then
1724 diff = 5
1725 end
1726 list:add(string.sub(addressList[a], 1, diff)..".. "..cap)
1727 end
1728 end
1729 end
1730 local res
1731 while running do
1732 if (refreshUI) then
1733 term:cleartb()
1734 statusBar:render()
1735 refreshUI = false
1736 end
1737 list:render()
1738 local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull())
1739 if (e == "listbox_select") then
1740 local addr = findAddress(p3, addressList)
1741 if (menuMethods(componentType, addr) == "quit") then
1742 res = "quit"
1743 running = nil
1744 else
1745 refreshUI = list:refresh()
1746 end
1747 elseif (e) then
1748 r = stdEvents(e, p1, p2, p3, p4, p5, componentType)
1749 if (r == "menu_back") then
1750 running = nil
1751 elseif (r == "menu_mount") then
1752 local addr = findAddress(list:getSelectedEntry(), addressList)
1753 if (menuMounts(addr, addressList) == "quit") then
1754 res = "quit"
1755 running = nil
1756 else
1757 refreshUI = list:refresh()
1758 end
1759 elseif (r == "ui_refresh") then
1760 refreshUI = list:refresh()
1761 elseif (r == "quit") then
1762 res = "quit"
1763 running = nil
1764 end
1765 end
1766 end -- while
1767 list:clear(true)
1768 list = nil
1769 statusBar = nil
1770 os.sleep(0)
1771 return res
1772end
1773
1774-- Menu: Component type selection
1775local function menuType()
1776 local running = true
1777 local refreshUI = true
1778 local cl = {}
1779 local ci = 1
1780 local statusBar = newStatusBar("&Quit", "&Help", 28, "View")
1781 local list = newList(1, 3, term.width, term.height - 4)
1782 while running do
1783 if (refreshComponents) then
1784 cl = getComponents()
1785 list:clear()
1786 for c = 1, #cl do
1787 list:add(cl[c][1])
1788 end
1789 refreshComponents = false
1790 refreshUI = true
1791 end
1792 if (refreshUI) then
1793 term:cleartb()
1794 statusBar:render()
1795 refreshUI = false
1796 end
1797 list:render()
1798 local e, p1, p2, p3, p4, p5 = list:checkEvent(event.pull())
1799 if (e == "listbox_select") then
1800 for c = 1, #cl do
1801 if (cl[c][1] == p3) then
1802 if (menuAddress(cl[c][1], cl[c][2]) == "quit") then
1803 running = nil
1804 else
1805 refreshUI = list:refresh()
1806 end
1807 break
1808 end
1809 end
1810 elseif (e) then
1811 local r = stdEvents(e, p1, p2, p3, p4, p5)
1812 if (r == "ui_refresh") then
1813 refreshUI = list:refresh()
1814 elseif (r == "quit") then
1815 running = nil
1816 end
1817 end
1818 end
1819 list:clear(true)
1820 list = nil
1821 statusBar = nil
1822 os.sleep(0)
1823end
1824
1825loadTheme()
1826if (term.width > 80 and term.height > 25) then
1827 term:init(80, 25)
1828end
1829menuType()
1830
1831-- Clean up globals
1832term:close()
1833term = nil -- clear: newTerm() object, not the term API!!
1834theme = nil
1835-- Clear library vars
1836components = nil
1837event = nil
1838keyb = nil
1839uni = nil
1840fs = nilpain