· 4 years ago · Aug 10, 2021, 01:52 AM
1-- This is eniallator's OS that is in the form of a GUI.
2-- The way it works is through left click navigation right click drop down menu's so to get started just right click in the black space to create new folders/apps
3-- If you want to try to get a better understanding of how it works, then i have completely commented my code so feel free to read it.
4-- You can also search for folders/files by simply typing, This will only handle lowercase a-z characters, numbers and also backspaces. It's lowercase because it will search with the apps as lowercase.
5-- For full details on features/changelogs i have a computercraft forums link that i update whenever i make a change - http://www.computercraft.info/forums2/index.php?/topic/25954-enios-the-interactive-gui-os/
6--
7-- ================================================================================
8--
9-- To make your own apps, you can either run this program once (it will create a folder from which you can use pastebin/ even edit in to make apps). Or you can right click on the screen (not on an app and when the program is running) and you can choose App from the drop down menu.
10-- This has alot of features and configurable options e.g app icon size or even the colour theme, so be sure to look below at the variables and config to your liking.
11
12-- =============================== User Config Below ==============================
13
14local folderDir = "eniOSApps"
15local osVersion = "1.5.2"
16-- Defining folderDir as the folder you keep your apps in and the osVersion is the version of the os you are using
17
18local editFile = ".EniOSEditFile"
19local runFile = ".EniOSRunFile"
20local compressedFileExtension = ".enicmprs"
21-- Defining the temporary files for compression and decompression aswell as the extension it uses
22
23local appMaxHeight = 3
24local appMaxNameLength = 11
25-- appMaxHeight is the height of the icons on the page and namelength will not only concatenate the apps to that value, it will also be the width of the icons on the page
26
27local pageNumber = 1
28-- Setting the page number that the program loads with
29
30local colours = {
31 app = colors.blue,
32 folder = colors.lightBlue,
33 addNew = colors.cyan,
34 page = colors.green,
35 dropMenuTop = colors.gray,
36 dropMenuBottom = colors.lightGray,
37 infoText = colors.lightBlue,
38 decoText = colors.yellow,
39 searchBackground = colors.cyan,
40 filterBackground = colors.purple,
41 filterOn = colors.lime,
42 filterOff = colors.red
43}
44-- Colour theme for the apps and the page icons
45
46-- ============================== End Of User Config ==============================
47
48if fs.exists(folderDir) and not fs.isDir(folderDir) then
49
50 error("folderDir variable is assigned to an already existing file.")
51end
52-- Seeing if the folderDir is a file already on the local computer
53
54if not fs.exists(folderDir) then
55
56 fs.makeDir(folderDir)
57end
58-- Making the folderDir if its not already made
59
60shell.run("clear")
61term.setTextColor(colours.infoText)
62print("Setting things up...")
63-- The boot up message
64
65local maxX,maxY = term.getSize()
66-- Getting the max dimensions of the screen
67
68local searchFilterName = "Filter"
69-- The word that is displayed where the filter is
70
71local activeFilterTags = {}
72-- Defining a table that i will be keeping the active filter tags in
73
74local terminate = false
75-- Defining the terminate variable as false to use later on
76
77local clipboard = nil
78-- Defining the clipBoard variable as nil for use later on
79
80local addNewVisible = true
81-- Setting the AddNew icon to visible
82
83local oldPullEvent = os.pullEvent
84os.pullEvent = os.pullEventRaw
85-- Defining os.pullEvent as os.pullEventRaw so that the read() function will use raw instead of the normal os.pullEvent
86
87local dropMenuPower = {
88 Terminate = function() terminate = true end,
89 Restart = function() os.reboot() end,
90 Shutdown = function() os.shutdown() end
91}
92-- Functions that are called when the user left clicks the power button on the top right
93
94local dropMenuFilterTags = {
95 Folders = function() checkFilterTags("Folders") end,
96 Files = function() checkFilterTags("Files") end,
97 Hidden = function() checkFilterTags("Hidden") end,
98 AddNew = function() checkAddNew() end
99}
100-- Tag Filters that will be displayed visually
101
102local dropMenuFolders = {
103 Delete = function(name) fs.delete(appFolder .. "/" .. name) end,
104 Copy = function(name) clipboard = {appFolder, name} end
105}
106-- Functions that are called when the user right clicks an option for a folder in the drop menu
107
108local dropMenuApps = {
109 Delete = function(name) fs.delete(appFolder .. "/" .. name) end,
110 Copy = function(name) clipboard = {appFolder, name} end,
111 Edit = function(name)
112
113 local tempFile = fs.open(editFile,"w")
114 tempFile.write(decompress(appFolder .. "/" .. name))
115 tempFile.close()
116 -- Preparing the temporary file with the file to edit's contents
117
118 shell.run("edit " .. editFile)
119 -- Editting the temporary file instead of the file from the file system
120
121 local currFile = fs.open(appFolder .. "/" .. name, "w")
122 currFile.write(compress(editFile))
123 currFile.close()
124 -- Writing the compressed version of the file to the file system
125
126 fs.delete(editFile)
127 end,
128 Rename = function(name)
129
130 clearScreen()
131 term.setTextColor(colours.infoText)
132 print("Enter the new name of the app below or press enter again to not rename:")
133 -- Prompting the user for an input
134
135 term.setTextColor(colors.white)
136 local newName = read() .. compressedFileExtension
137 -- Getting user input
138
139 term.setTextColor(colours.infoText)
140 -- Setting the text colour so i don't have to set it in the if statements
141
142 if not fs.exists(appFolder .. "/" .. newName) then
143 -- Checking if the name doesn't already exist in the current directory
144
145 fs.move(appFolder .. "/" .. name, appFolder .. "/" .. newName)
146 print("File successfully renamed")
147 sleep(1)
148 -- If the directory already doesn't have an app/folder of that name then it will rename the app
149
150 elseif newName == "" then
151 -- Checking if they have put anything in the read()
152
153 print("New name not set")
154 sleep(1)
155 -- If they haven't put anything in the read() then it won't try to set a new name
156 else
157
158 print("Name already exists in current directory")
159 sleep(1)
160 -- Informing the user that the name they chose already exists
161 end
162 end
163}
164-- Functions that are called when the user right clicks an option for an app in the drop menu
165
166local dropMenuOpts = {
167 App = function()
168
169 clearScreen()
170 eniOS()
171 -- Loading the OS version
172
173 term.setCursorPos(1,2)
174 term.setTextColor(colours.infoText)
175 print("Enter name of new app below or leave blank to not create:")
176 term.setTextColor(colors.white)
177 -- Prompting the user to enter the new app name
178
179 local appName = wordSplit(read())
180 -- Immediately splitting the user input at the spaces
181
182 if appName[1] and not fs.exists(appFolder .. "/" .. appName[1]) then
183 -- Checking if the user inputted a app name and seeing if it exists already or not
184
185 shell.run("edit " .. editFile)
186 -- Edit the new file
187
188 if fs.exists(editFile) then
189 -- Seeing if the user has made any changes to the editted temporary file
190
191 local userFile = fs.open(appFolder .. "/" .. appName[1] .. compressedFileExtension, "w")
192 userFile.write(compress(editFile))
193 userFile.close()
194 -- If the user has made changes to the temp file, it will compress the file and write the compressed version to the file system
195
196 fs.delete(editFile)
197 -- Deleting the temporary file
198 end
199
200 elseif appName[1] and fs.exists(appName[1]) then
201
202 term.setTextColor(colours.infoText)
203 print("Name already exists")
204 sleep(1)
205 -- Notifying the user that the name already exists
206 else
207
208 term.setTextColor(colours.infoText)
209 print("No new app created")
210 sleep(1)
211 -- If the user didn't enter something then it gives a message, sleeps (so the user can see the message) and then goes back to the app page
212 end
213 end,
214
215 Folder = function()
216
217 clearScreen()
218 eniOS()
219 -- Loading the OS version
220
221 term.setCursorPos(1,2)
222 term.setTextColor(colours.infoText)
223 print("Enter the name of the new folder below or leave blank to not create:")
224 term.setTextColor(colors.white)
225 -- Prompting the user for the new folder name
226
227 local folderName = wordSplit(read())
228 -- Immediately splitting the user input into a table
229
230 if folderName[1] and not fs.exists(folderName[1]) then
231 -- Checking if the user inputted a app name and seeing if it exists already or not
232
233 fs.makeDir(appFolder .. "/" .. folderName[1])
234 -- Makes the new folder
235
236 elseif folderName[1] and fs.exists(folderName[1]) then
237 -- Catches if the user has inputted an folder name that already exists
238
239 term.setTextColor(colours.infoText)
240 print("Name already exists")
241 sleep(1)
242 -- Notifying the user that the name they have inputted already exists
243 else
244
245 term.setTextColor(colours.infoText)
246 print("No new folder created")
247 sleep(1)
248 -- If the user didn't enter something then it gives a message, sleeps (so the user can see the message) and then goes back to the app page
249 end
250 end
251}
252-- The drop menu you see if you don't right click on an app
253
254if http then
255 -- First seeing if you have the http api enabled before doing anything
256
257 local testFile = http.get("http://pastebin.com/raw.php?i=DSMHN2iF")
258
259 if testFile then
260
261 testPaste = testFile.readAll()
262 testFile.close()
263 -- Reading a test file that i have made just to make sure you can access pastebin.com
264 end
265
266 if testPaste == "test" then
267 -- Seeing if the test paste has "test" in it's contents (should always return test)
268
269 dropMenuOpts.Pastebin = function()
270 -- Adding the Pastebin option to the dropMenuOpts
271
272 clearScreen()
273 eniOS()
274 -- Loading the OS version
275
276 term.setCursorPos(1,2)
277 term.setTextColor(colours.infoText)
278 print("Enter the pastebin url after http://pastebin.com/ below or leave blank to not create:")
279 term.setTextColor(colors.white)
280 -- Prompting the user for the pastebin url
281
282 local pasteURL = wordSplit(read())
283 -- Immediately splitting the user input into a table
284
285 if pasteURL[1] then
286 -- Seeing if the user has entered anything
287
288 local pasteFile = http.get("http://pastebin.com/raw.php?i=" .. textutils.urlEncode(pasteURL[1]))
289 local pasteContents = pasteFile.readAll()
290 pasteFile.close()
291 -- Getting the user's file from pastebin
292
293 if pasteContents then
294 -- Checking if the pastebin url is valid and is actually a file
295
296 term.setTextColor(colours.infoText)
297 print("Enter the file name you want to download as:")
298 term.setTextColor(colors.white)
299 -- Prompting the user for the file name to download as
300
301 local pasteFile = wordSplit(read())
302 -- Getting the file name and splitting the user input into a table
303
304 if pasteFile[1] and not fs.exists(appFolder .. "/" .. pasteFile[1]) then
305 -- Checking if the user has entered anything and also checking if the file already exists or not
306
307 local file = fs.open(appFolder .. "/" .. pasteFile[1], "w")
308 file.write(pasteContents)
309 file.close()
310 -- If everything goes well it will make the file
311
312 term.setTextColor(colours.infoText)
313 print("Successfully made file")
314 sleep(1)
315 -- Letting the user know it was successful
316
317 elseif pasteFile[1] and fs.exists(appFolder .. pasteFile[1]) then
318 -- Catching if the use has entered a file name but it already exists
319
320 term.setTextColor(colours.infoText)
321 print("File already exists. Press Y to force or enter another key to not force:")
322 local event, key, held = os.pullEvent("key")
323 -- Seeing if the user wants to force make the file
324
325 if key == 21 then
326 -- In this case 21 is the key Y
327
328 local file = fs.open(appFolder .. "/" .. pasteFile[1], "w")
329 file.write(pasteContents)
330 file.close()
331 -- Writing over the old file with the newly downloaded file
332
333 term.setTextColor(colours.infoText)
334 print("Successfully force made file")
335 sleep(1)
336 -- Letting the use know that the file was replaced
337 else
338
339 term.setTextColor(colours.infoText)
340 print("Aborted making the file")
341 sleep(1)
342 -- If the user hasn't entered a valid name e.g either spaces or nothing
343 end
344 end
345 else
346
347 term.setTextColor(infoText)
348 print("Invalid paste URL")
349 sleep(1)
350 -- Letting the user know that the pastebin url's contents aren't there e.g invalid url
351 end
352 else
353
354 term.setTextColor(colours.infoText)
355 print("No url specified")
356 sleep(1)
357 -- If no input was given it returns to the app page
358 end
359 end
360 end
361end
362-- First checking if the user can access pastebin.com and if they can then will add the pastebin option to dropMenuOpts
363
364local filterTags = {
365
366 Folders = function (tab)
367
368 local out = {}
369 -- Defining the out table to be later returned
370
371 for i=1, #tab do
372 -- Iterating for the amount of values the tab table has
373
374 if not fs.isDir(appFolder .. "/" .. tab[i]) then
375
376 table.insert(out,#out+1,tab[i])
377 end
378 -- Removing the folders from the table
379 end
380
381 return out
382 -- Returning the filtered table
383 end,
384
385 Files = function (tab)
386
387 local out = {}
388 -- Defining the out table to be later returned
389
390 for i=1,#tab do
391 -- Iterating for the amount of values the tab table has
392
393 if fs.isDir(appFolder .. "/" .. tab[i]) then
394
395 table.insert(out,#out+1,tab[i])
396 end
397 -- Removing the files from the table
398 end
399
400 return out
401 -- Returning the filtered table
402 end,
403
404 Hidden = function (tab)
405
406 local out = {}
407 -- Defining the out table to be later returned
408
409 for i=1,#tab do
410 -- Iterating for the amount of values the tab table has
411
412 if tab[i]:sub(1,1) ~= "." then
413
414 table.insert(out,#out+1,tab[i])
415 end
416 -- Adding the Filtered Files to the out table
417 end
418
419 return out
420 -- Returning the filtered table
421 end,
422
423 AddNew = function(tab) return tab end
424}
425-- The tags that the directory can be filtered for
426
427local numFilterTags = 0
428
429for key,val in pairs(dropMenuFilterTags) do
430
431 numFilterTags = numFilterTags + 1
432end
433-- Getting the number of filter tags for later use
434
435for key,val in pairs(dropMenuFilterTags) do
436 -- Iterating for the number of key/value pairs in dropMenuFilterTags
437
438 if key == "Hidden" then
439 -- Seeing if the key is "Hidden"
440
441 activeFilterTags[key] = true
442 -- Setting the key to activated
443 else
444
445 activeFilterTags[key] = false
446 -- Setting the key to deactivated
447 end
448end
449-- Making the activeFilterTags table
450
451local directoryBeforeSearch
452-- Declaring directoryBeforeSearch variable here so i can compare with it later on
453
454function checkFilterTags(name)
455 if activeFilterTags[name] then
456 -- Seeing what the status of the filter was before
457
458 activeFilterTags[name] = false
459 -- Removing the filter from the active tags list
460 else
461
462 activeFilterTags[name] = true
463 pageNumber = 1
464 -- Adding the filter to the tags list aswell as resetting the page number so it won't error
465 end
466end
467
468function checkAddNew()
469 if activeFilterTags.AddNew then
470 -- Seeing what the status of AddNew was before
471
472 activeFilterTags.AddNew = false
473 addNewVisible = true
474 -- Removing AddNew from the active tags list and setting it's visibility to true
475 else
476
477 activeFilterTags.AddNew = true
478 pageNumber = 1
479 addNewVisible = false
480 -- Adding AddNew to the tags list aswell as resetting the page number so it won't error and setting it's visibility to false
481 end
482end
483
484function fileSort(fileTab,appFolder,searchWord)
485
486 local out = {}
487 local files = {}
488
489 for key,val in pairs(activeFilterTags) do
490 -- iterating over the activeFilterTags table
491
492 if val then
493
494 fileTab = filterTags[key](fileTab)
495 -- Assigning fileTab to the new table that the filterTags tag will return
496 end
497 end
498 -- Applying any filters that the user has put on
499
500 directoryBeforeSearch = fileTab
501 -- Defining directoryBeforeSearch as fileTab before it has been filtered with the search word
502
503 for i=1,#fileTab do
504
505 local searchedName = fileTab[i]:lower():sub(1,#fileTab[i]-#compressedFileExtension)
506 -- Defining the searchedName variable for files
507
508 if fs.isDir(appFolder .. "/" .. fileTab[i]) and fileTab[i]:lower():find(searchWord) then
509 -- First iterating for the amount of items in the fileTab table and then checking if its a directory and putting it in the appropriate table
510
511 table.insert(out,#out+1,fileTab[i])
512
513 elseif searchedName:find(searchWord) then
514 if fileTab[i]:lower():sub(#fileTab[i]-#compressedFileExtension+1,#fileTab[i]) ~= compressedFileExtension then
515
516 local tempVarFile = fs.open(appFolder .. "/" .. fileTab[i],"r")
517 local tempVarFileContents = tempVarFile.readAll()
518 tempVarFile.close()
519
520 local tempFile = fs.open(editFile,"w")
521 tempFile.write(tempVarFileContents)
522 tempFile.close()
523
524 local actualFile = fs.open(appFolder .. "/" .. fileTab[i],"w")
525 actualFile.write(compress(editFile))
526 actualFile.close()
527
528 fs.delete(editFile)
529
530 fs.move(appFolder .. "/" .. fileTab[i], appFolder .. "/" .. fileTab[i] .. compressedFileExtension)
531 end
532
533 table.insert(files,#files+1,fileTab[i])
534 end
535 end
536 -- Sorting the fileTab table into the files and out table
537
538 for i=1,#files do
539
540 table.insert(out,#out+1,files[i])
541 end
542 -- merging the files table with the out table
543
544 if addNewVisible then
545 -- Checking if the visible var is true
546
547 out[#out+1] = "+"
548 -- Making the addNew icon
549 end
550
551 return out
552 -- Returning the sorted table
553end
554-- Function for sorting files and folders so that the folders will appear first
555
556function fileToLines(file)
557
558 local read = fs.open(file,"r")
559 local lines = {}
560
561 while true do
562
563 local currLine = read.readLine()
564
565 if currLine then
566
567 table.insert(lines, currLine)
568 else
569
570 break
571 end
572 end
573 -- While loop to keep on adding the current line to a table and if theres no current line, it will break
574
575 read.close()
576 return lines
577end
578-- Function to return a table of the lines of a file
579
580function wordSplit(string)
581
582 local out = {}
583
584 for word in string:gmatch("%S+") do
585 -- Splitting the string at the spaces
586
587 table.insert(out, word)
588 -- Inserting the value into a table
589 end
590
591 return out
592 -- Returning the table
593end
594-- Receives a string and returns a table that's the string split at the spaces
595
596function fillArea(tab)
597
598 term.setBackgroundColor(tab[5])
599 -- Setting the right background colour
600
601 for i=0,tab[4]-tab[2] do
602 -- Running for how many rows there are (indexes 2,4 == y values and indexes 1,3 == x values in the table)
603
604 local j = 0
605
606 while j <= tab[3]-tab[1] do
607 -- I use a while loop here because i iterate up with variable amounts
608
609 term.setCursorPos(tab[1]+j,tab[2]+i)
610 -- Positioning the cursor for the next string whether it be a space or the word
611
612 if tab[6] and math.floor(j-((tab[3]-tab[1])/2)+#tab[6]/2) == 0 and math.floor(i-(tab[4]-tab[2])/2) == 0 then
613 -- If statement seeing whether the iterators have hit the place where it should be writing the word out
614
615 term.write(tab[6])
616 j = j + #tab[6]
617 -- Only runs when the above if statement has found the place where the word will be centered
618 else
619
620 j = j + 1
621 term.write(" ")
622 -- Writing " " so the background text colour is visible
623 end
624 end
625 end
626end
627-- Defining the function for filling an area of the screen with a word centered in it
628
629function checkNum(num)
630
631 local out = ""
632
633 while num > 254 do
634
635 out = out .. string.char(0)
636 num = num - 254
637 end
638 -- While to iterate when num is bigger than 254
639
640 if num >= 13 then num = num + 1 end
641 -- Making sure num isn't byte 13
642
643 return out .. string.char(num)
644 -- Returning the bytes instead of the number.
645end
646-- Function that compression uses, if the number given in the arguments is bigger than 254 it will keep on adding the byte 0 to the return
647
648function compress(fileName)
649
650 local lines = fileToLines(fileName)
651 -- Splitting the file into it's lines in a table
652 local outTable = {{}}
653
654 for line=1,#lines do
655
656 local spaces = 0
657 local word = ""
658 outTable[line+1] = {}
659
660 local function sortWord(word)
661 if #word > 0 then
662
663 local wordFound = false
664
665 for i=1,#outTable[1] do
666 -- Iterating over the first index of outTable
667
668 if outTable[1][i] == word then
669 -- Checking if the word already exists or not
670
671 table.insert(outTable[line+1],i+3)
672 wordFound = true
673 break
674 end
675 end
676
677 if not wordFound then
678
679 table.insert(outTable[1],word)
680 table.insert(outTable[line+1],#outTable[1]+3)
681 end
682 -- Adding the word to the index if it hasn't been found
683
684 end
685 end
686 -- Function to add any new words to the index
687
688 local function sortSpaces(spaces)
689 if spaces > 0 then
690
691 if spaces > 1 then
692 -- Checking if the number of spaces is bigger than 1 so it can add a different byte depending on if it is or not
693
694 table.insert(outTable[line+1],2)
695 table.insert(outTable[line+1],spaces)
696 else
697
698 table.insert(outTable[line+1],3)
699 end
700 end
701 end
702 -- Function to handle spaces in the file
703
704 local currLine = ""
705
706 for i=1,#lines[line] do
707 -- For to handle the entire compression to convert the file into bytes
708
709 local currChar = lines[line]:sub(i,i)
710
711 if currChar == " " then
712 -- A crude way to split up the lines into words and spaces
713
714 spaces = spaces + 1
715 sortWord(word)
716 word = ""
717 else
718
719 word = word .. currChar
720 sortSpaces(spaces)
721 spaces = 0
722 end
723 end
724
725 sortWord(word)
726 sortSpaces(spaces)
727 end
728
729 local outString = ""
730
731 for i=1,#outTable do
732 -- For loop to combine the outTable into an output string
733
734 for j=1,#outTable[i] do
735 if i == 1 then
736 if #outString > 0 then outString = outString .. " " end
737
738 outString = outString .. outTable[i][j]
739 else
740
741 outString = outString .. checkNum(outTable[i][j])
742 end
743 end
744
745 if i == 1 then
746 -- Adding the new line character to the end of the line. The index is always at line 1 so i choose to add a \n to the end of line 1
747
748 outString = outString .. "\n"
749 else
750
751 outString = outString .. string.char(1)
752 end
753 end
754
755 return outString
756end
757-- The compression function
758
759function decompress(fileName)
760
761 local lines = fileToLines(fileName)
762 -- Splitting the file into it's lines in a table
763
764 local index = wordSplit(lines[1])
765 -- Seperating the index table from the body table
766
767 local body = {}
768 table.remove(lines,1)
769
770 for line=1,#lines do
771 -- For to convert the compressed file into it's original lines and where the indexes should go
772
773 if line > 1 then
774
775 table.insert(body,10)
776 end
777 -- Inserting the character 10 every time the file goes onto a new line. This is because character 10 actually is the new line character
778
779 for i=1,#lines[line] do
780 -- For loop to convert the bytes into the corresponding indexes
781
782 local indexNum = string.byte(lines[line]:sub(i))
783
784 if indexNum >= 13 then
785
786 indexNum = indexNum - 1
787 end
788
789 table.insert(body,indexNum)
790 end
791 end
792
793 local counter = 1
794 local fullFile = ""
795
796 while counter < #body do
797 -- While loop to convert the indexes into the corresponding words (aparts from the special characters)
798
799 if body[counter] == 0 then
800 -- Checking if the current index is 0 and then converting it into it's actual index (because 0 means it's bigger than 254)
801
802 local multiples = 0
803
804 while body[counter] == 0 do
805
806 counter = counter + 1
807 multiples = multiples + 254
808 end
809 -- Adding up the multiples of 254
810
811 fullFile = fullFile .. index[body[counter] + multiples-3]
812 -- Inserting the corresponding word with the full index from adding the multiples and the current index
813
814 elseif body[counter] == 1 then
815 -- Seeing if the current index is 1 which is the new line character
816
817 fullFile = fullFile .. "\n"
818
819 elseif body[counter] == 2 then
820 -- Seeing if the current index is 2 and then seeing what the next index is to make that next index * spaces.
821
822 counter = counter + 1
823
824 for i=1,body[counter] do
825
826 fullFile = fullFile .. " "
827 end
828 -- Iterating for the amount of spaces that should be in and adding them
829
830 elseif body[counter] == 3 then
831 -- Seeing if the current index is 3 and inserting a space into the file
832
833 fullFile = fullFile .. " "
834
835 else
836
837 fullFile = fullFile .. index[body[counter]-3]
838 end
839 -- If nothing before has caught the index, the corresponding word to that index will be inserted into the file.
840
841 counter = counter + 1
842 end
843
844 return fullFile
845end
846-- The decompression function
847
848function loadApps(appFolder,appFolderNav,searchWord)
849
850 local pages = {}
851 local apps = fileSort(fs.list(appFolder),appFolder,searchWord)
852 -- Getting the app names from the appFolder
853
854 local numberXApps = math.floor(maxX/(appMaxNameLength+1))
855 -- Seeing how many apps it can fit on one line
856
857 if appFolderNav[2] then
858
859 negY = 5
860 else
861
862 negY = 3
863 end
864 -- Seeing whether or not there needs to be more space for other icons on the page or not
865
866 local appsPerPage = math.floor((maxY-negY)/(appMaxHeight+1))*numberXApps
867 -- Seeing the total number of apps per page it can fit
868
869 for i=1,math.ceil(#apps/appsPerPage) do
870 -- Running for the number of pages it has to create
871
872 pages[i] = {}
873
874 for j=0,appsPerPage-1 do
875 -- Running for the number of apps per page
876
877 if not apps[1] then break end
878 -- Seeing if it has done all the apps in the table
879
880 local shortenedAppName = "+"
881 -- Defining the shortenedAppName variable locally here
882
883 if fs.isDir(appFolder .. "/" .. apps[1]) then
884
885 iconType = "folder"
886 colour = colours.folder
887 -- Setting the iconType var to folder and setting the colour
888
889 shortenedAppName = apps[1]:sub(1,appMaxNameLength)
890 -- Defining the shortened icon name for the app
891
892 elseif apps[1] == "+" then
893
894 iconType = "addNew"
895 colour = colours.addNew
896 -- Setting the iconType var to addNew and setting the colour
897
898 else
899
900 iconType = "file"
901 colour = colours.app
902 -- Setting the iconType var to file and setting the colour
903
904 shortenedAppName = apps[1]:sub(1,#apps[1]-#compressedFileExtension):sub(1,appMaxNameLength)
905 -- Defining the shortened icon name for the app
906 end
907 -- Seeing what icon colour to display for apps[1]
908
909 local indent = (j%numberXApps)*appMaxNameLength+(j%numberXApps)+2
910 -- Finding what the indent of the X value is
911
912 pages[i][j+1] = {indent,negY+math.floor(j/numberXApps)*(appMaxHeight+1),indent+appMaxNameLength-1,appMaxHeight+negY-1+math.floor(j/numberXApps)*(appMaxHeight+1),colour,shortenedAppName,apps[1],iconType}
913 -- Making the value for each app in a table in the following format: {x1,y1,x2,y2,background colour, substringed app name (for display purposes), original app name, icon type}
914
915 table.remove(apps,1)
916 -- Removing the last value from the table
917 end
918
919 if not apps[1] then break end
920 -- Seeing if it has done all the apps in the table
921 end
922
923 return pages
924 -- Returning the pages table after making it
925end
926-- Making a function to load the apps from the appFolder into a table thats split up into pages
927
928function eniOS()
929
930 term.setCursorPos(1,1)
931 term.setTextColor(colours.decoText)
932 term.write("EniOS Version: " .. osVersion)
933 term.setTextColor(colors.white)
934end
935-- Displaying the version number at the top left of the screen
936
937function otherArea(pageNumber)
938 return {
939 plus = {maxX-math.ceil((maxX)/2)+1,maxY-1,maxX,maxY-1,colours.page,"page "..pageNumber+1},
940 minus = {1,maxY-1,math.floor((maxX)/2)-1,maxY-1,colours.page,"page "..pageNumber-1},
941 power = {maxX-2,1,maxX,1,colors.red,"O"},
942 back = {1,3,2,3,colours.page,"<"},
943 searchBar = {1,maxY,maxX-#searchFilterName-1,maxY,colours.searchBackground},
944 searchFilter = {maxX-#searchFilterName-1,maxY,maxX,maxY,colours.filterBackground,searchFilterName}
945 }
946end
947-- Getting the updated pageNumber for the pages aswell as setting the power button at the top right of the screen
948
949function addPaste(tab)
950 if clipboard then
951
952 dropMenuOpts.paste = function() paste() end
953 end
954end
955-- Function to add the paste option once you have a clipboard from using copy
956
957function paste()
958 if appFolder ~= clipboard[1] then
959
960 fs.copy(clipboard[1] .. "/" .. clipboard[2], appFolder .. "/" .. clipboard[2])
961 end
962end
963-- Function for pasting a file
964
965function loadPage(pageNumber,appFolderNav,searchWord)
966
967 local otherAreaTab = otherArea(pageNumber)
968 -- Getting the otherArea table with the updated pageNumber
969
970 local topFolder = fs.list(folderDir)
971 -- Defining topFolder as the folders/files in the upper folder to be used later on
972
973 eniOS()
974 -- displaying the OS Version
975
976 if appFolderNav[2] then
977
978 term.setBackgroundColor(colors.black)
979 term.setTextColor(colours.infoText)
980 -- Getting the colours ready
981
982 local dirLink = "CC:" .. appFolderNav[2]
983 -- Defining the dirLink variable so it won't concatenate with what it was before
984
985 for i=1,#appFolderNav-2 do
986
987 dirLink = dirLink .. "/" .. appFolderNav[i+2]
988 end
989 -- Making the dirLink by conatenation without what folderDir is
990
991 if #dirLink > maxX-4 then
992
993 dirLink = dirLink:sub(#dirLink-maxX+4,#dirLink)
994 end
995
996 term.setCursorPos(4,3)
997 term.write(dirLink)
998 term.setTextColor(colors.white)
999 -- Seeing if dirLink is longer than the page and making a sub string if it is when writing it out
1000 end
1001 -- Seeing if the user is in a folder and then displaying the back button and the directory link
1002
1003 if appPages[1] then
1004 for i=1,#appPages[pageNumber] do
1005
1006 fillArea(appPages[pageNumber][i])
1007 end
1008 end
1009 -- Displaying the app icons on the screen
1010
1011 for key,val in pairs(otherAreaTab) do
1012 -- Iterating for all of the values in the otherAreaTab table
1013
1014 local fillCatch = true
1015 -- Defining a variable so i can use it later on
1016
1017 if key == "searchBar"then
1018 if directoryBeforeSearch[1] then
1019
1020 fillArea(val)
1021 end
1022
1023 fillCatch = false
1024 end
1025 -- Seeing if there are appPages then it will display the search bar
1026
1027 if key == "minus" then
1028 if appPages[pageNumber-1] then
1029
1030 fillArea(val)
1031 end
1032
1033 fillCatch = false
1034 end
1035 -- Seeing if theres a table before displaying the previous page icon
1036
1037 if key == "plus" then
1038 if appPages[pageNumber+1] then
1039
1040 fillArea(val)
1041 end
1042
1043 fillCatch = false
1044 end
1045 -- Seeing if theres a table before displaying the next page icon
1046
1047 if key == "back" then
1048 if appFolderNav[2] then
1049
1050 fillArea(val)
1051 end
1052
1053 fillCatch = false
1054 end
1055 -- Seeing if the user is in a folder to see if it should display the back icon
1056
1057 if fillCatch then
1058
1059 fillArea(val)
1060 end
1061 -- Catching if the other if statements haven't returned true
1062 end
1063
1064 if #searchWord > 0 then
1065 -- Checking if the searchWord has characters in it
1066
1067 term.setTextColor(colors.white)
1068 term.setBackgroundColor(colours.searchBackground)
1069 term.setCursorPos(2,maxY)
1070 -- Getting ready for the text to be written
1071
1072 local searchWordWrite = searchWord
1073 -- Defining another variable as searchWord so that it won't substring searchWord
1074
1075 if #searchWord > maxX-5-#searchFilterName then
1076 -- Seeing if the searchWord string is longer than the screen
1077
1078 searchWordWrite = searchWord:sub(#searchWord-maxX+5+#searchFilterName,#searchWord)
1079 -- Making a substring of searchWord so that it will fit on the string
1080 end
1081
1082 term.write(searchWordWrite)
1083 term.setCursorBlink(true)
1084 term.setTextColor(colors.white)
1085 -- Writing searchWordWrite to the screen and then resetting the text colour
1086
1087 elseif directoryBeforeSearch[1] then
1088
1089 term.setCursorPos(2,maxY)
1090 term.setBackgroundColor(colours.searchBackground)
1091 term.setTextColor(colors.white)
1092 term.write("Type to search")
1093 -- Making place holder text for the search bar
1094
1095 elseif not topFolder[1] and activeFilterTags.AddNew then
1096
1097 term.setCursorPos(1,3)
1098 term.setBackgroundColor(colors.black)
1099 term.setTextColor(colours.dropMenuBottom)
1100 print("Right click to start making apps/folders")
1101 term.setTextColor(colors.white)
1102 -- Welcoming the user into the program and telling them how to get started
1103 end
1104end
1105-- Loads the entire page onto the screen using fillArea alot
1106
1107function clickedApp(pageNumber,clickX,clickY)
1108 if appPages[1] then
1109 for i=1, #appPages[pageNumber] do
1110 -- Iterating for the number of apps in the page
1111
1112 if appPages[pageNumber][i][1] <= clickX and appPages[pageNumber][i][3] >= clickX and appPages[pageNumber][i][2] <= clickY and appPages[pageNumber][i][4] >= clickY then
1113 -- Comparing the clicked x and y values with each of the app's x and y values
1114
1115 return appPages[pageNumber][i]
1116 -- returning the original app name in the appPages table
1117 end
1118 end
1119 end
1120
1121 return false
1122 -- Returning false if it hasn't already returned a value
1123end
1124-- Detecting what app the user has clicked (if they have clicked one that is)
1125
1126function clickedOther(pageNumber,clickX,clickY)
1127 for key,val in pairs(otherArea(pageNumber)) do
1128 -- Iterating for the length of the table the otherArea() function returns
1129
1130 if val[1] <= clickX and val[3] >= clickX and val[2] <= clickY and val[4] >= clickY then
1131 -- Comparing the clicked x and y values with each of the icon's x and y values
1132
1133 return key
1134 -- Returning the key of the key/value pair in the otherArea table
1135 end
1136 end
1137
1138 return false
1139 -- Returning false if the func hasn't already returned a value
1140end
1141-- Detecting if the user has clicked the page icons or the close icon
1142
1143function clearScreen()
1144
1145 term.setTextColor(colors.white)
1146 term.setBackgroundColor(colors.black)
1147 term.setCursorBlink(false)
1148 shell.run("clear")
1149end
1150-- Clearing the screen and resetting the taxt colour and the background colour
1151
1152function loadDropMenu(dropMenuOptionsFuncs,name,x,y,filterBool,isCreate)
1153
1154 local dropMenuOptions = {}
1155
1156 for key,_ in pairs(dropMenuOptionsFuncs) do
1157
1158 table.insert(dropMenuOptions,1,key)
1159 end
1160 -- Making the dropMenuOptions table from the functions's keys
1161
1162 if y + #dropMenuOptions > maxY then
1163
1164 y = y - (y+#dropMenuOptions-maxY)
1165 end
1166 -- Seeing if the drop menu will go off the screen and correcting it if it does
1167
1168 if x+appMaxNameLength > maxX then
1169
1170 x = x - (x+appMaxNameLength-maxX)
1171 end
1172 -- Seeing if the drop menu will go off the screen and correcting it if it does
1173
1174 if name then
1175
1176 term.setCursorPos(x,y)
1177 fillArea({x,y,x+appMaxNameLength,y,colours.dropMenuTop})
1178 term.setCursorPos(x,y)
1179
1180 local shortenedName
1181
1182 if not fs.isDir(appFolder .. "/" .. name) and not isCreate then
1183
1184 shortenedName = name:sub(1,#name-#compressedFileExtension):sub(1,appMaxNameLength)
1185 else
1186
1187 shortenedName = name:sub(1,appMaxNameLength)
1188 end
1189 -- Defining shortenedName differently depending on whether its a file or a folder
1190
1191 term.write(shortenedName)
1192 -- Writing the app name in the right colour and at the right place
1193 end
1194 -- Seeing if the name argument has been given
1195
1196 fillArea({x,y+1,x+appMaxNameLength,y+#dropMenuOptions,colours.dropMenuBottom})
1197 -- Filling the below area for the drop menu options
1198
1199 for i=1,#dropMenuOptions do
1200 -- Iterating through the dropMenuOptions table
1201
1202 if filterBool and activeFilterTags[dropMenuOptions[i]] then
1203 -- Seeing if filterBool is true and if the filter is active
1204
1205 term.setTextColor(colours.filterOn)
1206 -- Setting the text colour to filterOn if the filter is on
1207
1208 elseif filterBool then
1209 -- Seeing if filterBool is true but the filter is not active
1210
1211 term.setTextColor(colours.filterOff)
1212 -- Setting the text colour to filterOff if the filter is off
1213 end
1214
1215 term.setCursorPos(x,y+i)
1216 term.write(dropMenuOptions[i]:sub(1,appMaxNameLength))
1217 term.setTextColor(colors.white)
1218 end
1219 -- Writing the options in their places and making sure they don't go over the appMaxNameLength
1220
1221 return dropMenuOptions,dropMenuOptionsFuncs,name,x,y
1222 -- Returning the changed coordinates of the drop menu
1223end
1224-- Loading the drop menu from where the user has clicked
1225
1226function dropMenu(dropMenuOptions,dropMenuOptionsFuncs,name,x,y)
1227
1228 local event, button, clickX, clickY = os.pullEvent("mouse_click")
1229 -- Getting a user mouse click input
1230
1231 if button == 1 then
1232 -- Seeing if the user has left clicked
1233
1234 for i=1,#dropMenuOptions do
1235 --Iterating for the amount of options in the drop menu
1236
1237 if x <= clickX and x+appMaxNameLength >= clickX and y+i <= clickY and y+i >= clickY then
1238 -- Detecting whether the user has clicked an option
1239
1240 dropMenuOptionsFuncs[dropMenuOptions[i]](name)
1241 -- Running the function in the drop menu
1242 end
1243 end
1244 end
1245end
1246-- Function for detecting if the user has clicked an option in the drop menu
1247
1248appFolderNav = {folderDir}
1249-- Making the nav table so its easy to modify the directory navigation
1250
1251local event = "mouse_click"
1252-- Giving the event var a value
1253
1254local searchWord = ""
1255-- Defining searchWord as an empty string
1256
1257while not terminate do
1258 -- Running while the terminate variable is false
1259
1260 appFolder = ""
1261 -- Defining the appFolder as an empty string before adding strings to it
1262 for i=1,#appFolderNav do
1263
1264 appFolder = appFolder .. "/" .. appFolderNav[i]
1265 end
1266 -- Remaking the appFolder directory navigation
1267
1268 appPages = loadApps(appFolder,appFolderNav,searchWord)
1269 -- Loading the apps into a sorted table
1270
1271 if event == "mouse_click" or event == "key" then
1272
1273 clearScreen()
1274 loadPage(pageNumber,appFolderNav,searchWord)
1275 end
1276 -- First checking if the last event was a mouse click or a key press (to make it that little bit more efficient) before clearing and then loading the current page
1277
1278 if not dropMenuOpts.paste then
1279
1280 addPaste()
1281 end
1282 -- while the paste option is not in the dropMenuOpts table it tries to add paste to dropMenuOpts
1283
1284 local event, button, x, y = os.pullEvent()
1285 -- Waiting for a mouse click event
1286
1287 local currentFolder = fs.list(appFolder)
1288 -- Getting whatever is in the current folder for later use
1289
1290 if event == "mouse_click" then
1291
1292 local clickedAppResult = clickedApp(pageNumber,x,y)
1293 local clickedOtherResult = clickedOther(pageNumber,x,y)
1294 -- Getting the result of each of the click detection functions
1295
1296 if button == 1 then
1297 if clickedAppResult and clickedAppResult[8] == "file" then
1298
1299 local toRun = fs.open(runFile,"w")
1300 toRun.write(decompress(appFolder .. "/" .. clickedAppResult[7]))
1301 toRun.close()
1302 -- Making a temporary file with the decompressed version of the file's contents
1303
1304 clearScreen()
1305 os.pullEvent = oldPullEvent
1306 shell.run(runFile .. " " .. appFolder)
1307 os.pullEvent = os.pullEventRaw
1308 clearScreen()
1309 fs.delete(runFile)
1310 -- If the user has clicked an app, it will first clear the screen and then run the app and then clear the screen yet again
1311
1312 elseif clickedAppResult and clickedAppResult[8] == "folder" then
1313
1314 table.insert(appFolderNav,#appFolderNav+1,clickedAppResult[7])
1315 -- When the user clicks a folder it will add the folder name to the appFolderNav table
1316
1317 elseif clickedAppResult and clickedAppResult[8] == "addNew" then
1318
1319 dropMenu(loadDropMenu(dropMenuOpts,nil,x,y-1))
1320 -- Loading the create drop menu when the user left clicks the + icon
1321
1322 elseif appFolderNav[2] and clickedOtherResult == "back" then
1323
1324 table.remove(appFolderNav,#appFolderNav)
1325 -- when the user clicks the back button inside a folder it will remove the last folder name the user clicked
1326
1327 elseif appPages[pageNumber-1] and clickedOtherResult == "minus" then
1328
1329 pageNumber = pageNumber - 1
1330 -- If the user has clicked the left page icon and if the appPages page exists it will minus the current pageNumber
1331
1332 elseif appPages[pageNumber+1] and clickedOtherResult == "plus" then
1333
1334 pageNumber = pageNumber + 1
1335 -- If the user has clicked the right page icon and if the appPages page exists it will plus the current pageNumber
1336
1337 elseif clickedOtherResult == "searchFilter" then
1338
1339 dropMenu(loadDropMenu(dropMenuFilterTags,nil,x,y-1-numFilterTags,true))
1340 -- Loading the filter's drop menu
1341
1342 elseif clickedOtherResult == "power" then
1343
1344 dropMenu(loadDropMenu(dropMenuPower,nil,x,y))
1345 -- If the user has clicked the power icon, it will load the power drop menu
1346
1347 if terminate then
1348
1349 clearScreen()
1350 end
1351 -- Clearing the screen when the user terminates the OS to go back to the normal CC OS
1352
1353 end
1354
1355 if clickedOtherResult ~= "minus" and clickedOtherResult ~= "plus" then
1356
1357 searchWord = ""
1358 end
1359 -- making the searchWord = nothing after you search
1360
1361 elseif button == 2 and clickedAppResult and clickedAppResult[8] == "file" then
1362
1363 dropMenu(loadDropMenu(dropMenuApps,clickedAppResult[7],x,y))
1364 -- First loading the drop menu for apps and then detecting if the user has clicked on it
1365
1366 elseif button == 2 and clickedAppResult and clickedAppResult[8] == "folder" then
1367
1368 dropMenu(loadDropMenu(dropMenuFolders,clickedAppResult[7],x,y))
1369 -- First loading the drop menu for folders and then detecting if the user has clicked on it
1370
1371 elseif button == 2 then
1372
1373 dropMenu(loadDropMenu(dropMenuOpts,"Create",x,y,false,true))
1374 -- If the user hasn't right clicked on an app they get this drop down menu
1375 end
1376 elseif event == "key" and currentFolder[1] then
1377 -- Seeing if the event is a key press for searching
1378
1379 pageNumber = 1
1380 -- Resetting the page number so that the program doesn't error
1381
1382 local pressedKey = button
1383 local held = x
1384 -- Redefining button and x to make sense for the key event
1385
1386 if pressedKey == 14 then
1387 -- Checking if the user has pressed backspace
1388
1389 if #searchWord > 0 then
1390 -- Checking if theres any more characters to delete in the searchWord
1391
1392 searchWord = searchWord:sub(1,#searchWord-1)
1393 -- Removing the last character from the searchWord string
1394 end
1395
1396 elseif pressedKey <= 10 and pressedKey >= 2 then
1397 -- Catching if the user has entered 1-9
1398
1399 searchWord = searchWord .. pressedKey - 1
1400 -- Adding the correct number to the searchWord
1401
1402 elseif pressedKey == 11 then
1403 -- Catching if the user has pressed 0
1404
1405 searchWord = searchWord .. 0
1406 -- Adding 0 to the searchWord
1407
1408 elseif pressedKey == 203 and appPages[pageNumber-1] then
1409
1410 pageNumber = pageNumber - 1
1411 -- If you press the left arrow key it will try to minus 1 from pageNumber
1412
1413 elseif pressedKey == 205 and appPages[pageNumber+1] then
1414
1415 pageNumber = pageNumber + 1
1416 -- If you press the right arrow key it will try to add 1 to pageNumber
1417
1418 else
1419 for key,val in pairs(keys) do
1420 -- Iterating for the amount of keys and values the keys api has
1421
1422 if val == pressedKey then
1423 -- Checking if what the user entered matched the current val
1424
1425 if #key == 1 then
1426 -- Seeing if the val is a single character, essentially making this only pick up a-z
1427
1428 searchWord = searchWord .. key
1429 -- Adding the key to the searchWord string
1430 end
1431
1432 break
1433 -- Breaking the for loop to prevent further unecessary iteration
1434 end
1435 end
1436 end
1437 end
1438end