SHOW:
|
|
- or go back to the newest paste.
1 | if not fs.exists('lex') then | |
2 | shell.run('pastebin get edyuQ5xY lex') | |
3 | end | |
4 | ||
5 | local lexSuccess = os.loadAPI('lex') | |
6 | ||
7 | if not lexSuccess or not lex or not lex.lex then | |
8 | print('A valid lexer is required (file `lex` seems to be broken or not a lexer)') | |
9 | return | |
10 | end | |
11 | ||
12 | -- Get file to edit | |
13 | local tArgs = { ... } | |
14 | if #tArgs == 0 then | |
15 | print( "Usage: shedit <path>" ) | |
16 | return | |
17 | end | |
18 | ||
19 | -- Error checking | |
20 | local sPath = shell.resolve( tArgs[1] ) | |
21 | local bReadOnly = fs.isReadOnly( sPath ) | |
22 | if fs.exists( sPath ) and fs.isDir( sPath ) then | |
23 | print( "Cannot edit a directory." ) | |
24 | return | |
25 | end | |
26 | ||
27 | local x,y = 1,1 | |
28 | local markX, markY = 1, 1 | |
29 | local w,h = term.getSize() | |
30 | local scrollX, scrollY = 0,0 | |
31 | ||
32 | local tLines = {} | |
33 | local bRunning = true | |
34 | ||
35 | -- Colors | |
36 | local highlightColor, keywordColor, commentColor, textColor, bgColor, stringColor | |
37 | if term.isColor() then | |
38 | bgColor = colors.black | |
39 | textColor = colors.white | |
40 | highlightColor = colors.yellow | |
41 | keywordColor = colors.yellow | |
42 | commentColor = colors.green | |
43 | stringColor = colors.red | |
44 | else | |
45 | bgColor = colors.black | |
46 | textColor = colors.white | |
47 | highlightColor = colors.white | |
48 | keywordColor = colors.white | |
49 | commentColor = colors.white | |
50 | stringColor = colors.white | |
51 | end | |
52 | ||
53 | -- Menus | |
54 | local bMenu = false | |
55 | local nMenuItem = 1 | |
56 | local tMenuItems = {} | |
57 | if not bReadOnly then | |
58 | table.insert( tMenuItems, "Save" ) | |
59 | end | |
60 | if shell.openTab then | |
61 | table.insert( tMenuItems, "Run" ) | |
62 | end | |
63 | if peripheral.find( "printer" ) then | |
64 | table.insert( tMenuItems, "Print" ) | |
65 | end | |
66 | table.insert( tMenuItems, "Exit" ) | |
67 | table.insert(tMenuItems, 'Tools') | |
68 | table.insert(tMenuItems, 'Find') | |
69 | table.insert(tMenuItems, 'Jump') | |
70 | table.insert(tMenuItems, 'Copy') | |
71 | table.insert(tMenuItems, 'VPaste') | |
72 | ||
73 | local sStatus = "Press Ctrl to access menu" | |
74 | if string.len( sStatus ) > w - 5 then | |
75 | sStatus = "Press Ctrl for menu" | |
76 | end | |
77 | ||
78 | local function load( _sPath ) | |
79 | tLines = {} | |
80 | if fs.exists( _sPath ) then | |
81 | local file = io.open( _sPath, "r" ) | |
82 | local sLine = file:read() | |
83 | while sLine do | |
84 | table.insert( tLines, sLine ) | |
85 | sLine = file:read() | |
86 | end | |
87 | file:close() | |
88 | end | |
89 | ||
90 | if #tLines == 0 then | |
91 | table.insert( tLines, "" ) | |
92 | end | |
93 | end | |
94 | ||
95 | local function save( _sPath ) | |
96 | -- Create intervening folder | |
97 | local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() ) | |
98 | if not fs.exists( sDir ) then | |
99 | fs.makeDir( sDir ) | |
100 | end | |
101 | ||
102 | -- Save | |
103 | local file = nil | |
104 | local function innerSave() | |
105 | file = fs.open( _sPath, "w" ) | |
106 | if file then | |
107 | for n, sLine in ipairs( tLines ) do | |
108 | file.write( sLine .. "\n" ) | |
109 | end | |
110 | else | |
111 | error( "Failed to open ".._sPath ) | |
112 | end | |
113 | end | |
114 | ||
115 | local ok, err = pcall( innerSave ) | |
116 | if file then | |
117 | file.close() | |
118 | end | |
119 | return ok, err | |
120 | end | |
121 | ||
122 | local tKeywords = { | |
123 | ["and"] = true, | |
124 | ["break"] = true, | |
125 | ["do"] = true, | |
126 | ["else"] = true, | |
127 | ["elseif"] = true, | |
128 | ["end"] = true, | |
129 | ["false"] = true, | |
130 | ["for"] = true, | |
131 | ["function"] = true, | |
132 | ["if"] = true, | |
133 | ["in"] = true, | |
134 | ["local"] = true, | |
135 | ["nil"] = true, | |
136 | ["not"] = true, | |
137 | ["or"] = true, | |
138 | ["repeat"] = true, | |
139 | ["return"] = true, | |
140 | ["then"] = true, | |
141 | ["true"] = true, | |
142 | ["until"]= true, | |
143 | ["while"] = true, | |
144 | } | |
145 | ||
146 | local function tryWrite( sLine, regex, color ) | |
147 | local match = string.match( sLine, regex ) | |
148 | if match then | |
149 | if type(color) == "number" then | |
150 | term.setTextColor( color ) | |
151 | else | |
152 | term.setTextColor( color(match) ) | |
153 | end | |
154 | term.write( match ) | |
155 | term.setTextColor( textColor ) | |
156 | return string.sub( sLine, string.len(match) + 1 ) | |
157 | end | |
158 | return nil | |
159 | end | |
160 | ||
161 | --[[local function writeHighlighted( sLine ) | |
162 | while string.len(sLine) > 0 do | |
163 | sLine = | |
164 | tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColor ) or | |
165 | tryWrite( sLine, "^%-%-.*", commentColor ) or | |
166 | tryWrite( sLine, "^\"\"", stringColor ) or | |
167 | tryWrite( sLine, "^\".-[^\\]\"", stringColor ) or | |
168 | tryWrite( sLine, "^\'\'", stringColor ) or | |
169 | tryWrite( sLine, "^\'.-[^\\]\'", stringColor ) or | |
170 | tryWrite( sLine, "^%[%[.-%]%]", stringColor ) or | |
171 | tryWrite( sLine, "^[%w_]+", function( match ) | |
172 | if tKeywords[ match ] then | |
173 | return keywordColor | |
174 | end | |
175 | return textColor | |
176 | end ) or | |
177 | tryWrite( sLine, "^[^%w_]", textColor ) | |
178 | end | |
179 | end]] | |
180 | ||
181 | local apis = { | |
182 | -- tables | |
183 | bit = true, | |
184 | bit32 = true, | |
185 | bitop = true, | |
186 | colors = true, | |
187 | colours = true, | |
188 | coroutine = true, | |
189 | disk = true, | |
190 | fs = true, | |
191 | gps = true, | |
192 | help = true, | |
193 | http = true, | |
194 | io = true, | |
195 | keys = true, | |
196 | math = true, | |
197 | os = true, | |
198 | paintutils = true, | |
199 | parallel = true, | |
200 | peripheral = true, | |
201 | rednet = true, | |
202 | redstone = true, | |
203 | rs = true, | |
204 | settings = true, | |
205 | shell = true, | |
206 | socket = true, | |
207 | string = true, | |
208 | table = true, | |
209 | term = true, | |
210 | textutils = true, | |
211 | vector = true, | |
212 | window = true, | |
213 | ||
214 | -- functions | |
215 | assert = true, | |
216 | collectgarbage = true, | |
217 | dofile = true, | |
218 | error = true, | |
219 | getfenv = true, | |
220 | getmetatable = true, | |
221 | ipairs = true, | |
222 | loadfile = true, | |
223 | loadstring = true, | |
224 | module = true, | |
225 | next = true, | |
226 | pairs = true, | |
227 | pcall = true, | |
228 | print = true, | |
229 | rawequal = true, | |
230 | rawget = true, | |
231 | rawset = true, | |
232 | require = true, | |
233 | select = true, | |
234 | setfenv = true, | |
235 | setmetatable = true, | |
236 | tonumber = true, | |
237 | tostring = true, | |
238 | type = true, | |
239 | unpack = true, | |
240 | xpcall = true, | |
241 | printError = true, | |
242 | write = true | |
243 | } | |
244 | ||
245 | local function onUpdate() | |
246 | tokenized = lex.lex(table.concat(tLines, '\n')) | |
247 | end | |
248 | ||
249 | local function markExists() | |
250 | if markX ~= x or markY ~= y then | |
251 | return true | |
252 | else | |
253 | return false | |
254 | end | |
255 | end | |
256 | ||
257 | local function isMarked(newX, newY) | |
258 | if (newY > markY and newY < y) or (newY > y and newY < markY) then | |
259 | return true | |
260 | end | |
261 | ||
262 | if newY == markY and newY == y then | |
263 | if markX > x and newX >= x and newX < markX then | |
264 | return true | |
265 | elseif markX < x and newX >= markX and newX < x then | |
266 | return true | |
267 | end | |
268 | elseif newY == markY then | |
269 | if newX < markX and y < markY then | |
270 | return true | |
271 | elseif newX >= markX and y > markY then | |
272 | return true | |
273 | end | |
274 | elseif newY == y then | |
275 | if newX < x and y > markY then | |
276 | return true | |
277 | elseif newX >= x and y < markY then | |
278 | return true | |
279 | end | |
280 | end | |
281 | ||
282 | return false | |
283 | end | |
284 | ||
285 | local function getMarks() | |
286 | local msx, msy | |
287 | local mex, mey | |
288 | ||
289 | if markY == y then | |
290 | if markX > x then | |
291 | msx = x | |
292 | msy = y | |
293 | mex = markX | |
294 | mey = markY | |
295 | else | |
296 | msx = markX | |
297 | msy = markY | |
298 | mex = x | |
299 | mey = y | |
300 | end | |
301 | else | |
302 | if markY > y then | |
303 | msx = x | |
304 | msy = y | |
305 | mex = markX | |
306 | mey = markY | |
307 | else | |
308 | msx = markX | |
309 | msy = markY | |
310 | mex = x | |
311 | mey = y | |
312 | end | |
313 | end | |
314 | ||
315 | return msx, msy, mex, mey | |
316 | end | |
317 | ||
318 | local function getCharDisplay(char) | |
319 | if char == '\t' then | |
320 | return ' ' | |
321 | else | |
322 | return char | |
323 | end | |
324 | end | |
325 | ||
326 | local function getCharWidth(char) | |
327 | return getCharDisplay(char):len() | |
328 | end | |
329 | ||
330 | local function writeHighlighted(lineNr) | |
331 | local tokens = tokenized[lineNr] or {} | |
332 | local textColor = term.getTextColor() | |
333 | local bgColor = term.getBackgroundColor() | |
334 | local setX, setY = term.getCursorPos() | |
335 | local msx, msy, mex, mey = getMarks() | |
336 | ||
337 | if lineNr < mey and lineNr >= msy then | |
338 | term.setBackgroundColor(colors.white) | |
339 | end | |
340 | ||
341 | term.clearLine() | |
342 | term.setCursorPos(setX, setY) | |
343 | ||
344 | for t = 1, #tokens do | |
345 | local token = tokens[t] | |
346 | local color = colors.white | |
347 | ||
348 | if token.type == 'keyword' or token.type == 'operator' then | |
349 | color = colors.yellow | |
350 | elseif token.type == 'number' or token.type == 'value' then | |
351 | color = colors.purple | |
352 | elseif token.type == 'comment' then | |
353 | color = colors.green | |
354 | elseif token.type == 'ident' and apis[token.data] then | |
355 | color = colors.lightGray | |
356 | elseif token.type == 'string' then | |
357 | color = colors.cyan | |
358 | elseif token.type == 'escape' then | |
359 | color = colors.purple | |
360 | elseif token.type == 'unidentified' then | |
361 | color = colors.red | |
362 | end | |
363 | ||
364 | for p = 1, #token.data do | |
365 | local sub = getCharDisplay(token.data:sub(p, p)) | |
366 | ||
367 | local cX, cY = p + token.posFirst - 1, lineNr | |
368 | ||
369 | if (cY == msy and cY == mey and cX >= msx and cX < mex) or (cY == msy and cY ~= mey and cX >= msx) or (cY == mey and cY ~= msy and cX < mex) or (cY > msy and cY < mey) then | |
370 | term.setTextColor(colors.black) | |
371 | term.setBackgroundColor(color) | |
372 | else | |
373 | term.setBackgroundColor(colors.black) | |
374 | term.setTextColor(color) | |
375 | end | |
376 | ||
377 | term.write(sub) | |
378 | end | |
379 | end | |
380 | ||
381 | term.setTextColor(textColor) | |
382 | term.setBackgroundColor(bgColor) | |
383 | end | |
384 | ||
385 | local tCompletions | |
386 | local nCompletion | |
387 | ||
388 | local tCompleteEnv = _ENV | |
389 | local function complete( sLine ) | |
390 | if settings.get( "edit.autocomplete" ) then | |
391 | local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" ) | |
392 | if nStartPos then | |
393 | sLine = string.sub( sLine, nStartPos ) | |
394 | end | |
395 | if #sLine > 0 then | |
396 | return textutils.complete( sLine, tCompleteEnv ) | |
397 | end | |
398 | end | |
399 | return nil | |
400 | end | |
401 | ||
402 | local function recomplete() | |
403 | local sLine = tLines[y] | |
404 | if not bMenu and not bReadOnly and x == string.len(sLine) + 1 then | |
405 | tCompletions = complete( sLine ) | |
406 | if tCompletions and #tCompletions > 0 then | |
407 | nCompletion = 1 | |
408 | else | |
409 | nCompletion = nil | |
410 | end | |
411 | else | |
412 | tCompletions = nil | |
413 | nCompletion = nil | |
414 | end | |
415 | end | |
416 | ||
417 | local function writeCompletion( sLine ) | |
418 | if nCompletion then | |
419 | local sCompletion = tCompletions[ nCompletion ] | |
420 | term.setTextColor( colors.white ) | |
421 | term.setBackgroundColor( colors.gray ) | |
422 | term.write( sCompletion ) | |
423 | term.setTextColor( textColor ) | |
424 | term.setBackgroundColor( bgColor ) | |
425 | end | |
426 | end | |
427 | ||
428 | local function getCursorX(lineNr) | |
429 | local line = tLines[lineNr] | |
430 | local cx = 1 | |
431 | ||
432 | for i = 1, x - 1 do | |
433 | cx = cx + getCharWidth(line:sub(i, i)) | |
434 | end | |
435 | ||
436 | return cx | |
437 | end | |
438 | ||
439 | local function cxToPos(lineNr, cx) | |
440 | local line = tLines[lineNr] | |
441 | local num = 1 | |
442 | local pos = 1 | |
443 | ||
444 | for i = 1, #line do | |
445 | amt = getCharWidth(line:sub(i, i)) | |
446 | ||
447 | if num + amt > cx then | |
448 | return pos | |
449 | else | |
450 | num = num + amt | |
451 | pos = pos + 1 | |
452 | end | |
453 | end | |
454 | ||
455 | return pos | |
456 | end | |
457 | ||
458 | local function redrawText() | |
459 | local cursorX, cursorY = x, y | |
460 | for y=1,h-1 do | |
461 | term.setCursorPos( 1 - scrollX, y ) | |
462 | term.clearLine() | |
463 | ||
464 | local sLine = tLines[ y + scrollY ] | |
465 | if sLine ~= nil then | |
466 | --writeHighlighted( sLine ) | |
467 | writeHighlighted(y + scrollY) | |
468 | if cursorY == y and cursorX == #sLine + 1 then | |
469 | writeCompletion() | |
470 | end | |
471 | end | |
472 | end | |
473 | ||
474 | local cx = getCursorX(y) | |
475 | ||
476 | term.setCursorPos( cx - scrollX, y - scrollY ) | |
477 | end | |
478 | ||
479 | --[[local function redrawLine(_nY) | |
480 | local sLine = tLines[_nY] | |
481 | if sLine then | |
482 | term.setCursorPos( 1 - scrollX, _nY - scrollY ) | |
483 | term.clearLine() | |
484 | --writeHighlighted( sLine ) | |
485 | writeHighlighted(_nY) | |
486 | if _nY == y and x == #sLine + 1 then | |
487 | writeCompletion() | |
488 | end | |
489 | term.setCursorPos( x - scrollX, _nY - scrollY ) | |
490 | end | |
491 | end]] | |
492 | ||
493 | local redrawLine = redrawText | |
494 | ||
495 | local function redrawMenu() | |
496 | -- Clear line | |
497 | term.setCursorPos( 1, h ) | |
498 | term.clearLine() | |
499 | ||
500 | -- Draw line numbers | |
501 | --[[term.setCursorPos( w - string.len( "Ln "..y ) + 1, h ) | |
502 | term.setTextColor( highlightColor ) | |
503 | term.write( "Ln " ) | |
504 | term.setTextColor( textColor ) | |
505 | term.write( y )]] | |
506 | ||
507 | if not bMenu then | |
508 | -- Draw status | |
509 | term.setTextColor( highlightColor ) | |
510 | term.write( sStatus ) | |
511 | term.setTextColor( textColor ) | |
512 | ||
513 | term.setCursorPos(w - ('[' .. x .. ':' .. y .. ']:[' .. markX .. ':' .. markY .. ']'):len() + 1, h) | |
514 | term.setTextColor(highlightColor) | |
515 | term.write('[') | |
516 | term.setTextColor(textColor) | |
517 | term.write(x) | |
518 | term.setTextColor(highlightColor) | |
519 | term.write(':') | |
520 | term.setTextColor(textColor) | |
521 | term.write(y) | |
522 | term.setTextColor(highlightColor) | |
523 | term.write(']:[') | |
524 | term.setTextColor(textColor) | |
525 | term.write(markX) | |
526 | term.setTextColor(highlightColor) | |
527 | term.write(':') | |
528 | term.setTextColor(textColor) | |
529 | term.write(markY) | |
530 | term.setTextColor(highlightColor) | |
531 | term.write(']') | |
532 | term.setTextColor(textColor) | |
533 | else | |
534 | -- Draw menu | |
535 | term.setTextColor( textColor ) | |
536 | for nItem,sItem in pairs( tMenuItems ) do | |
537 | if nItem == nMenuItem then | |
538 | term.setTextColor( highlightColor ) | |
539 | term.write( "[" ) | |
540 | term.setTextColor( textColor ) | |
541 | term.write( sItem ) | |
542 | term.setTextColor( highlightColor ) | |
543 | term.write( "]" ) | |
544 | term.setTextColor( textColor ) | |
545 | else | |
546 | term.write( " "..sItem.." " ) | |
547 | end | |
548 | end | |
549 | end | |
550 | ||
551 | -- Reset cursor | |
552 | term.setCursorPos( getCursorX(y) - scrollX, y - scrollY ) | |
553 | end | |
554 | ||
555 | local function setCursor( newX, newY ) | |
556 | local oldX, oldY = x, y | |
557 | x, y = newX, newY | |
558 | local screenX = getCursorX(y) - scrollX | |
559 | local screenY = y - scrollY | |
560 | ||
561 | local bRedraw = false | |
562 | if screenX < 1 then | |
563 | scrollX = getCursorX(y) - 1 | |
564 | screenX = 1 | |
565 | bRedraw = true | |
566 | elseif screenX > w then | |
567 | scrollX = scrollX + (screenX - w) | |
568 | screenX = w | |
569 | bRedraw = true | |
570 | end | |
571 | ||
572 | if screenY < 1 then | |
573 | scrollY = y - 1 | |
574 | screenY = 1 | |
575 | bRedraw = true | |
576 | elseif screenY > h-1 then | |
577 | scrollY = y - (h-1) | |
578 | screenY = h-1 | |
579 | bRedraw = true | |
580 | end | |
581 | ||
582 | recomplete() | |
583 | if bRedraw then | |
584 | redrawText() | |
585 | elseif y ~= oldY then | |
586 | redrawLine( oldY ) | |
587 | redrawLine( y ) | |
588 | else | |
589 | redrawLine( y ) | |
590 | end | |
591 | term.setCursorPos( screenX, screenY ) | |
592 | ||
593 | redrawMenu() | |
594 | end | |
595 | ||
596 | local function setCursorMark(newX, newY) | |
597 | markX, markY = newX, newY | |
598 | setCursor(newX, newY) | |
599 | end | |
600 | ||
601 | local function deleteMarked() | |
602 | if markExists() then | |
603 | local msx, msy, mex, mey = getMarks() | |
604 | ||
605 | if msy == mey then | |
606 | local line = tLines[mey] | |
607 | ||
608 | tLines[mey] = line:sub(1, msx - 1) .. line:sub(mex) | |
609 | else | |
610 | if mey - msy > 1 then | |
611 | for i = 1, mey - msy - 1 do | |
612 | table.remove(tLines, msy + 1) | |
613 | end | |
614 | end | |
615 | ||
616 | local line = tLines[msy] | |
617 | local line2 = tLines[msy + 1] | |
618 | ||
619 | tLines[msy] = line:sub(1, msx - 1) .. line2:sub(mex) | |
620 | table.remove(tLines, msy + 1) | |
621 | end | |
622 | ||
623 | setCursorMark(msx, msy) | |
624 | end | |
625 | end | |
626 | ||
627 | local function getMarked() | |
628 | if markExists() then | |
629 | local msx, msy, mex, mey = getMarks() | |
630 | ||
631 | if msy == mey then | |
632 | return tLines[msy]:sub(msx, mex - 1) | |
633 | else | |
634 | local marked = tLines[msy]:sub(msx) | |
635 | ||
636 | if mey - msy > 1 then | |
637 | for i = msy + 1, mey - 1 do | |
638 | marked = marked .. '\n' .. tLines[i] | |
639 | end | |
640 | end | |
641 | ||
642 | marked = marked .. '\n' .. tLines[mey]:sub(1, mex - 1) | |
643 | ||
644 | return marked | |
645 | end | |
646 | end | |
647 | end | |
648 | ||
649 | local toolsFuncs = { | |
650 | { | |
651 | 'Convert 2 spaces to tabs', | |
652 | function() | |
653 | for i = 1, #tLines do | |
654 | local line = tLines[i] | |
655 | local count = 0 | |
656 | ||
657 | while true do | |
658 | if line:sub(count * 2 + 1, count * 2 + 2) == ' ' then | |
659 | count = count + 1 | |
660 | else | |
661 | break | |
662 | end | |
663 | end | |
664 | ||
665 | tLines[i] = ('\t'):rep(count) .. line:sub(count * 2 + 1) | |
666 | end | |
667 | end | |
668 | }, { | |
669 | 'Convert tabs to 2 spaces', | |
670 | function() | |
671 | for i = 1, #tLines do | |
672 | local line = tLines[i] | |
673 | local count = 0 | |
674 | ||
675 | while true do | |
676 | if line:sub(count + 1, count + 1) == '\t' then | |
677 | count = count + 1 | |
678 | else | |
679 | break | |
680 | end | |
681 | end | |
682 | ||
683 | tLines[i] = (' '):rep(count) .. line:sub(count + 1) | |
684 | end | |
685 | end | |
686 | } | |
687 | } | |
688 | ||
689 | -- for testing scrolling, etc in the tools menu | |
690 | --[[for i = 1, 50 do | |
691 | table.insert(toolsFuncs, {'Do nothing ' .. tostring(i), function() end}) | |
692 | end]] | |
693 | ||
694 | ||
695 | local findHistory = {} | |
696 | local jumpHistory = {} | |
697 | local vClipboard = '' | |
698 | ||
699 | local tMenuFuncs = { | |
700 | Save = function() | |
701 | if bReadOnly then | |
702 | sStatus = "Access denied" | |
703 | else | |
704 | local ok, err = save( sPath ) | |
705 | if ok then | |
706 | sStatus="Saved to "..sPath | |
707 | else | |
708 | sStatus="Error saving to "..sPath | |
709 | end | |
710 | end | |
711 | redrawMenu() | |
712 | end, | |
713 | Print = function() | |
714 | local printer = peripheral.find( "printer" ) | |
715 | if not printer then | |
716 | sStatus = "No printer attached" | |
717 | return | |
718 | end | |
719 | ||
720 | local nPage = 0 | |
721 | local sName = fs.getName( sPath ) | |
722 | if printer.getInkLevel() < 1 then | |
723 | sStatus = "Printer out of ink" | |
724 | return | |
725 | elseif printer.getPaperLevel() < 1 then | |
726 | sStatus = "Printer out of paper" | |
727 | return | |
728 | end | |
729 | ||
730 | local screenTerminal = term.current() | |
731 | local printerTerminal = { | |
732 | getCursorPos = printer.getCursorPos, | |
733 | setCursorPos = printer.setCursorPos, | |
734 | getSize = printer.getPageSize, | |
735 | write = printer.write, | |
736 | } | |
737 | printerTerminal.scroll = function() | |
738 | if nPage == 1 then | |
739 | printer.setPageTitle( sName.." (page "..nPage..")" ) | |
740 | end | |
741 | ||
742 | while not printer.newPage() do | |
743 | if printer.getInkLevel() < 1 then | |
744 | sStatus = "Printer out of ink, please refill" | |
745 | elseif printer.getPaperLevel() < 1 then | |
746 | sStatus = "Printer out of paper, please refill" | |
747 | else | |
748 | sStatus = "Printer output tray full, please empty" | |
749 | end | |
750 | ||
751 | term.redirect( screenTerminal ) | |
752 | redrawMenu() | |
753 | term.redirect( printerTerminal ) | |
754 | ||
755 | local timer = os.startTimer(0.5) | |
756 | sleep(0.5) | |
757 | end | |
758 | ||
759 | nPage = nPage + 1 | |
760 | if nPage == 1 then | |
761 | printer.setPageTitle( sName ) | |
762 | else | |
763 | printer.setPageTitle( sName.." (page "..nPage..")" ) | |
764 | end | |
765 | end | |
766 | ||
767 | bMenu = false | |
768 | term.redirect( printerTerminal ) | |
769 | local ok, error = pcall( function() | |
770 | term.scroll() | |
771 | for n, sLine in ipairs( tLines ) do | |
772 | print( sLine ) | |
773 | end | |
774 | end ) | |
775 | term.redirect( screenTerminal ) | |
776 | if not ok then | |
777 | print( error ) | |
778 | end | |
779 | ||
780 | while not printer.endPage() do | |
781 | sStatus = "Printer output tray full, please empty" | |
782 | redrawMenu() | |
783 | sleep( 0.5 ) | |
784 | end | |
785 | bMenu = true | |
786 | ||
787 | if nPage > 1 then | |
788 | sStatus = "Printed "..nPage.." Pages" | |
789 | else | |
790 | sStatus = "Printed 1 Page" | |
791 | end | |
792 | redrawMenu() | |
793 | end, | |
794 | Exit = function() | |
795 | bRunning = false | |
796 | end, | |
797 | Run = function() | |
798 | local sTempPath = "/.temp" | |
799 | local ok, err = save( sTempPath ) | |
800 | if ok then | |
801 | local nTask = shell.openTab( sTempPath ) | |
802 | if nTask then | |
803 | shell.switchTab( nTask ) | |
804 | else | |
805 | sStatus="Error starting Task" | |
806 | end | |
807 | fs.delete( sTempPath ) | |
808 | else | |
809 | sStatus="Error saving to "..sTempPath | |
810 | end | |
811 | redrawMenu() | |
812 | end, | |
813 | Tools = function() | |
814 | bMenu = false | |
815 | redrawText() | |
816 | redrawMenu() | |
817 | ||
818 | local bgColor = term.getBackgroundColor() | |
819 | paintutils.drawFilledBox(2, 2, w - 1, h - 1, colors.gray) | |
820 | term.setCursorPos(3, 3) | |
821 | term.write('ShEdit Tools') | |
822 | ||
823 | local scrollY = 0 | |
824 | local height = h - 8 | |
825 | local offset = 5 | |
826 | local selected = 1 | |
827 | local run = false | |
828 | ||
829 | term.setTextColor(colors.lightGray) | |
830 | term.setCursorPos(3, offset + height + 1) | |
831 | term.write('Arrows to select. Enter to run. Ctrl to cancel.') | |
832 | ||
833 | while true do | |
834 | term.setBackgroundColor(colors.gray) | |
835 | term.setTextColor(colors.lightGray) | |
836 | local toWrite = '(' .. tostring(selected) .. '/' .. tostring(#toolsFuncs) .. ')' | |
837 | term.setCursorPos(w - toWrite:len() - 1, 3) | |
838 | term.write(toWrite) | |
839 | term.setTextColor(colors.white) | |
840 | ||
841 | for i = 1, height do | |
842 | local index = i + scrollY | |
843 | term.setTextColor(colors.white) | |
844 | ||
845 | if selected == index then | |
846 | term.setTextColor(keywordColor) | |
847 | end | |
848 | ||
849 | if index <= #toolsFuncs then | |
850 | paintutils.drawLine(2, offset + i - 1, w - 1, offset + i - 1, color) | |
851 | term.setCursorPos(3, offset + index - scrollY - 1) | |
852 | ||
853 | if selected == index then | |
854 | term.write('> ') | |
855 | else | |
856 | term.write(' ') | |
857 | end | |
858 | ||
859 | term.write(toolsFuncs[index][1]) | |
860 | end | |
861 | end | |
862 | ||
863 | local evt, arg1, arg2, arg3 = os.pullEvent() | |
864 | local clampScroll = false | |
865 | ||
866 | if evt == 'key' then | |
867 | clampScroll = true | |
868 | ||
869 | if arg1 == keys.up then | |
870 | if selected == 1 then | |
871 | selected = #toolsFuncs | |
872 | else | |
873 | selected = selected - 1 | |
874 | end | |
875 | elseif arg1 == keys.down then | |
876 | if selected == #toolsFuncs then | |
877 | selected = 1 | |
878 | else | |
879 | selected = selected + 1 | |
880 | end | |
881 | elseif arg1 == keys.pageUp then | |
882 | selected = math.max(1, math.min(#toolsFuncs, selected - height)) | |
883 | elseif arg1 == keys.pageDown then | |
884 | selected = math.max(1, math.min(#toolsFuncs, selected + height)) | |
885 | elseif arg1 == keys.enter then | |
886 | run = true | |
887 | ||
888 | break | |
889 | elseif arg1 == keys.leftCtrl then | |
890 | break | |
891 | else | |
892 | clampScroll = false | |
893 | end | |
894 | elseif evt == 'mouse_click' then | |
895 | if arg1 == 1 then | |
896 | if arg2 > 1 and arg2 < w then | |
897 | if arg3 >= offset and arg3 < offset + height then | |
898 | local index = arg3 - offset + scrollY + 1 | |
899 | ||
900 | if index <= #toolsFuncs then | |
901 | selected = index | |
902 | run = true | |
903 | ||
904 | break | |
905 | end | |
906 | end | |
907 | end | |
908 | end | |
909 | elseif evt == 'mouse_scroll' then | |
910 | if arg2 > 1 and arg2 < w then | |
911 | if arg3 >= offset and arg3 < offset + height then | |
912 | scrollY = math.max(0, math.min(#toolsFuncs - height, scrollY + arg1)) | |
913 | end | |
914 | end | |
915 | end | |
916 | ||
917 | if clampScroll == true then | |
918 | if selected > height + scrollY then | |
919 | scrollY = selected - height | |
920 | elseif selected <= scrollY then | |
921 | scrollY = selected - 1 | |
922 | end | |
923 | end | |
924 | end | |
925 | ||
926 | term.setBackgroundColor(bgColor) | |
927 | redrawText() | |
928 | bMenu = true | |
929 | ||
930 | if run then | |
931 | if not bReadOnly then | |
932 | bMenu = false | |
933 | redrawMenu() | |
934 | toolsFuncs[selected][2]() | |
935 | onUpdate() | |
936 | redrawText() | |
937 | bMenu = true | |
938 | else | |
939 | sStatus = 'File is read-only' | |
940 | end | |
941 | end | |
942 | end, | |
943 | Find = function() | |
944 | term.setCursorPos(1, h) | |
945 | term.clearLine() | |
946 | ||
947 | term.setTextColor(highlightColor) | |
948 | term.write('Find: ') | |
949 | term.setTextColor(textColor) | |
950 | ||
951 | local text = read(nil, findHistory) | |
952 | table.insert(findHistory, text) | |
953 | ||
954 | local fLine, fPos | |
955 | local found = false | |
956 | ||
957 | for i = y, #tLines do | |
958 | local searchText = tLines[i] | |
959 | ||
960 | if i == y then | |
961 | searchText = searchText:sub(x + 1) | |
962 | end | |
963 | ||
964 | local searchPat = '' | |
965 | ||
966 | local escapedChars = { | |
967 | ['%'] = true, ['.'] = true, ['('] = true, [')'] = true, | |
968 | ['%'] = true, ['+'] = true, ['-'] = true, ['*'] = true, | |
969 | ['?'] = true, ['['] = true, ['^'] = true, ['$'] = true | |
970 | } | |
971 | ||
972 | for i = 1, #text do | |
973 | local char = text:sub(i, i) | |
974 | ||
975 | if escapedChars[char] == true then | |
976 | searchPat = searchPat .. '%' | |
977 | end | |
978 | ||
979 | searchPat = searchPat .. char | |
980 | end | |
981 | ||
982 | local pos = searchText:find(searchPat) | |
983 | ||
984 | if pos then | |
985 | fLine = i | |
986 | fPos = pos | |
987 | ||
988 | if i == y then | |
989 | fPos = fPos + x | |
990 | end | |
991 | ||
992 | found = true | |
993 | ||
994 | break | |
995 | end | |
996 | end | |
997 | ||
998 | if not found then | |
999 | sStatus = 'Found no matches' | |
1000 | else | |
1001 | setCursorMark(fPos, fLine) | |
1002 | setCursor(fPos + text:len(), fLine) | |
1003 | sStatus = 'Found a match on line ' .. tostring(fLine) | |
1004 | end | |
1005 | ||
1006 | redrawText() | |
1007 | end, | |
1008 | Jump = function() | |
1009 | term.setCursorPos(1, h) | |
1010 | term.clearLine() | |
1011 | ||
1012 | term.setTextColor(highlightColor) | |
1013 | term.write('Jump: ') | |
1014 | term.setTextColor(textColor) | |
1015 | ||
1016 | local toJump = read(nil, jumpHistory) | |
1017 | table.insert(jumpHistory, toJump) | |
1018 | ||
1019 | local num = tonumber(toJump) | |
1020 | ||
1021 | if not num then | |
1022 | sStatus = 'Invalid line' | |
1023 | else | |
1024 | if num % 1 == 0 then | |
1025 | if num >= 1 and num <= #tLines then | |
1026 | setCursorMark(1, num) | |
1027 | sStatus = 'Successfully jumped to line ' .. toJump | |
1028 | else | |
1029 | sStatus = 'Line is not in the range 1 - ' .. tostring(#tLines) | |
1030 | end | |
1031 | else | |
1032 | sStatus = 'Line must be an integer' | |
1033 | end | |
1034 | end | |
1035 | ||
1036 | redrawText() | |
1037 | end, | |
1038 | Copy = function() | |
1039 | vClipboard = getMarked() | |
1040 | ||
1041 | local lines = 1 | |
1042 | ||
1043 | for i = 1, #vClipboard do | |
1044 | if vClipboard:sub(i, i) == '\n' then | |
1045 | lines = lines + 1 | |
1046 | end | |
1047 | end | |
1048 | ||
1049 | sStatus = 'Copied ' .. tostring(lines) .. ' lines' | |
1050 | end, | |
1051 | VPaste = function() | |
1052 | if not bReadOnly then | |
1053 | deleteMarked() | |
1054 | local lines = 1 | |
1055 | ||
1056 | for i = 1, #vClipboard do | |
1057 | local char = vClipboard:sub(i, i) | |
1058 | ||
1059 | if char == '\n' then | |
1060 | lines = lines + 1 | |
1061 | local sLine = tLines[y] | |
1062 | tLines[y] = string.sub(sLine,1,x-1) | |
1063 | table.insert( tLines, y+1, string.sub(sLine,x) ) | |
1064 | x = 1 | |
1065 | y = y + 1 | |
1066 | else | |
1067 | tLines[y] = tLines[y]:sub(1, x - 1) .. char .. tLines[y]:sub(x) | |
1068 | x = x + 1 | |
1069 | end | |
1070 | end | |
1071 | ||
1072 | setCursorMark(x, y) | |
1073 | onUpdate() | |
1074 | redrawText() | |
1075 | ||
1076 | sStatus = 'Pasted ' .. tostring(lines) .. ' lines' | |
1077 | else | |
1078 | sStatus = 'File is read-only' | |
1079 | end | |
1080 | end | |
1081 | } | |
1082 | ||
1083 | local function doMenuItem( _n ) | |
1084 | tMenuFuncs[tMenuItems[_n]]() | |
1085 | if bMenu then | |
1086 | bMenu = false | |
1087 | term.setCursorBlink( true ) | |
1088 | end | |
1089 | redrawMenu() | |
1090 | end | |
1091 | ||
1092 | -- Actual program functionality begins | |
1093 | load(sPath) | |
1094 | ||
1095 | term.setBackgroundColor( bgColor ) | |
1096 | term.clear() | |
1097 | term.setCursorPos(getCursorX(y), y) | |
1098 | term.setCursorBlink( true ) | |
1099 | ||
1100 | recomplete() | |
1101 | onUpdate() | |
1102 | redrawText() | |
1103 | redrawMenu() | |
1104 | ||
1105 | local function acceptCompletion() | |
1106 | if nCompletion then | |
1107 | -- Append the completion | |
1108 | local sCompletion = tCompletions[ nCompletion ] | |
1109 | tLines[y] = tLines[y] .. sCompletion | |
1110 | onUpdate() | |
1111 | setCursorMark( x + string.len( sCompletion ), y ) | |
1112 | end | |
1113 | end | |
1114 | ||
1115 | local holdingShift = false | |
1116 | ||
1117 | -- Handle input | |
1118 | while bRunning do | |
1119 | local sEvent, param, param2, param3 = os.pullEvent() | |
1120 | if sEvent == "key" then | |
1121 | local oldX, oldY = x, y | |
1122 | if param == keys.up then | |
1123 | local cursorSet = setCursorMark | |
1124 | ||
1125 | if holdingShift then | |
1126 | cursorSet = setCursor | |
1127 | end | |
1128 | ||
1129 | -- Up | |
1130 | if not bMenu then | |
1131 | if nCompletion then | |
1132 | -- Cycle completions | |
1133 | nCompletion = nCompletion - 1 | |
1134 | if nCompletion < 1 then | |
1135 | nCompletion = #tCompletions | |
1136 | end | |
1137 | redrawLine(y) | |
1138 | ||
1139 | elseif y > 1 then | |
1140 | -- Move cursor up | |
1141 | --[[cursorSet( | |
1142 | math.min( x, string.len( tLines[y - 1] ) + 1 ), | |
1143 | y - 1 | |
1144 | )]] | |
1145 | ||
1146 | local cx = getCursorX(y) | |
1147 | local pos = cxToPos(y - 1, cx) | |
1148 | ||
1149 | cursorSet(pos, y - 1) | |
1150 | else | |
1151 | cursorSet(1, y) | |
1152 | end | |
1153 | end | |
1154 | ||
1155 | elseif param == keys.down then | |
1156 | local cursorSet = setCursorMark | |
1157 | ||
1158 | if holdingShift then | |
1159 | cursorSet = setCursor | |
1160 | end | |
1161 | ||
1162 | -- Down | |
1163 | if not bMenu then | |
1164 | -- Move cursor down | |
1165 | if nCompletion then | |
1166 | -- Cycle completions | |
1167 | nCompletion = nCompletion + 1 | |
1168 | if nCompletion > #tCompletions then | |
1169 | nCompletion = 1 | |
1170 | end | |
1171 | redrawLine(y) | |
1172 | ||
1173 | elseif y < #tLines then | |
1174 | -- Move cursor down | |
1175 | --[[cursorSet( | |
1176 | math.min( x, string.len( tLines[y + 1] ) + 1 ), | |
1177 | y + 1 | |
1178 | )]] | |
1179 | ||
1180 | local cx = getCursorX(y) | |
1181 | local pos = cxToPos(y + 1, cx) | |
1182 | ||
1183 | cursorSet(pos, y + 1) | |
1184 | else | |
1185 | cursorSet(#tLines[y] + 1, y) | |
1186 | end | |
1187 | end | |
1188 | ||
1189 | elseif param == keys.tab then | |
1190 | -- Tab | |
1191 | if not bMenu and not bReadOnly then | |
1192 | if nCompletion and x == string.len(tLines[y]) + 1 then | |
1193 | -- Accept autocomplete | |
1194 | acceptCompletion() | |
1195 | else | |
1196 | local msx, msy, mex, mey = getMarks() | |
1197 | ||
1198 | for i = msy, mey do | |
1199 | local line = tLines[i] | |
1200 | ||
1201 | if holdingShift then | |
1202 | -- Unindent line | |
1203 | if line:sub(1, 1) == '\t' then | |
1204 | line = line:sub(2) | |
1205 | end | |
1206 | else | |
1207 | -- Indent line | |
1208 | if markExists() then | |
1209 | line = '\t' .. line | |
1210 | else | |
1211 | line = line:sub(1, x - 1) .. '\t' .. line:sub(x) | |
1212 | end | |
1213 | end | |
1214 | ||
1215 | tLines[i] = line | |
1216 | end | |
1217 | ||
1218 | if holdingShift then | |
1219 | markX = math.max(1, markX - 1) | |
1220 | setCursor(math.max(1, x - 1), y) | |
1221 | else | |
1222 | markX = markX + 1 | |
1223 | setCursor(x + 1, y) | |
1224 | end | |
1225 | ||
1226 | onUpdate() | |
1227 | redrawText() | |
1228 | end | |
1229 | end | |
1230 | ||
1231 | elseif param == keys.pageUp then | |
1232 | local cursorSet = setCursorMark | |
1233 | ||
1234 | if holdingShift then | |
1235 | cursorSet = setCursor | |
1236 | end | |
1237 | ||
1238 | -- Page Up | |
1239 | if not bMenu then | |
1240 | -- Move up a page | |
1241 | local newY | |
1242 | if y - (h - 1) >= 1 then | |
1243 | newY = y - (h - 1) | |
1244 | else | |
1245 | newY = 1 | |
1246 | end | |
1247 | cursorSet( | |
1248 | math.min( x, string.len( tLines[newY] ) + 1 ), | |
1249 | newY | |
1250 | ) | |
1251 | end | |
1252 | ||
1253 | elseif param == keys.pageDown then | |
1254 | local cursorSet = setCursorMark | |
1255 | ||
1256 | if holdingShift then | |
1257 | cursorSet = setCursor | |
1258 | end | |
1259 | ||
1260 | -- Page Down | |
1261 | if not bMenu then | |
1262 | -- Move down a page | |
1263 | local newY | |
1264 | if y + (h - 1) <= #tLines then | |
1265 | newY = y + (h - 1) | |
1266 | else | |
1267 | newY = #tLines | |
1268 | end | |
1269 | local newX = math.min( x, string.len( tLines[newY] ) + 1 ) | |
1270 | cursorSet( newX, newY ) | |
1271 | end | |
1272 | ||
1273 | elseif param == keys.home then | |
1274 | local cursorSet = setCursorMark | |
1275 | ||
1276 | if holdingShift then | |
1277 | cursorSet = setCursor | |
1278 | end | |
1279 | ||
1280 | -- Home | |
1281 | if not bMenu then | |
1282 | -- Move cursor to the beginning | |
1283 | if x > 1 then | |
1284 | cursorSet(1,y) | |
1285 | end | |
1286 | end | |
1287 | ||
1288 | elseif param == keys["end"] then | |
1289 | local cursorSet = setCursorMark | |
1290 | ||
1291 | if holdingShift then | |
1292 | cursorSet = setCursor | |
1293 | end | |
1294 | ||
1295 | -- End | |
1296 | if not bMenu then | |
1297 | -- Move cursor to the end | |
1298 | local nLimit = string.len( tLines[y] ) + 1 | |
1299 | if x < nLimit then | |
1300 | cursorSet( nLimit, y ) | |
1301 | end | |
1302 | end | |
1303 | ||
1304 | elseif param == keys.left then | |
1305 | local cursorSet = setCursorMark | |
1306 | ||
1307 | if holdingShift then | |
1308 | cursorSet = setCursor | |
1309 | end | |
1310 | ||
1311 | -- Left | |
1312 | if not bMenu then | |
1313 | if x > 1 then | |
1314 | -- Move cursor left | |
1315 | cursorSet( x - 1, y ) | |
1316 | elseif x==1 and y>1 then | |
1317 | cursorSet( string.len( tLines[y-1] ) + 1, y - 1 ) | |
1318 | end | |
1319 | else | |
1320 | -- Move menu left | |
1321 | nMenuItem = nMenuItem - 1 | |
1322 | if nMenuItem < 1 then | |
1323 | nMenuItem = #tMenuItems | |
1324 | end | |
1325 | redrawMenu() | |
1326 | end | |
1327 | ||
1328 | elseif param == keys.right then | |
1329 | local cursorSet = setCursorMark | |
1330 | ||
1331 | if holdingShift then | |
1332 | cursorSet = setCursor | |
1333 | end | |
1334 | ||
1335 | -- Right | |
1336 | if not bMenu then | |
1337 | local nLimit = string.len( tLines[y] ) + 1 | |
1338 | if x < nLimit then | |
1339 | -- Move cursor right | |
1340 | cursorSet( x + 1, y ) | |
1341 | elseif nCompletion and x == string.len(tLines[y]) + 1 then | |
1342 | -- Accept autocomplete | |
1343 | acceptCompletion() | |
1344 | elseif x==nLimit and y<#tLines then | |
1345 | -- Go to next line | |
1346 | cursorSet( 1, y + 1 ) | |
1347 | end | |
1348 | else | |
1349 | -- Move menu right | |
1350 | nMenuItem = nMenuItem + 1 | |
1351 | if nMenuItem > #tMenuItems then | |
1352 | nMenuItem = 1 | |
1353 | end | |
1354 | redrawMenu() | |
1355 | end | |
1356 | ||
1357 | elseif param == keys.delete then | |
1358 | -- Delete | |
1359 | if not bMenu and not bReadOnly then | |
1360 | if markExists() then | |
1361 | deleteMarked() | |
1362 | else | |
1363 | local nLimit = string.len( tLines[y] ) + 1 | |
1364 | if x < nLimit then | |
1365 | local sLine = tLines[y] | |
1366 | tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1) | |
1367 | elseif y<#tLines then | |
1368 | tLines[y] = tLines[y] .. tLines[y+1] | |
1369 | table.remove( tLines, y+1 ) | |
1370 | end | |
1371 | end | |
1372 | ||
1373 | onUpdate() | |
1374 | recomplete() | |
1375 | redrawText() | |
1376 | end | |
1377 | ||
1378 | elseif param == keys.backspace then | |
1379 | -- Backspace | |
1380 | if not bMenu and not bReadOnly then | |
1381 | if markExists() then | |
1382 | deleteMarked() | |
1383 | else | |
1384 | if x > 1 then | |
1385 | -- Remove character | |
1386 | local sLine = tLines[y] | |
1387 | tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x) | |
1388 | setCursorMark( x - 1, y ) | |
1389 | elseif y > 1 then | |
1390 | -- Remove newline | |
1391 | local sPrevLen = string.len( tLines[y-1] ) | |
1392 | tLines[y-1] = tLines[y-1] .. tLines[y] | |
1393 | table.remove( tLines, y ) | |
1394 | setCursorMark( sPrevLen + 1, y - 1 ) | |
1395 | end | |
1396 | end | |
1397 | ||
1398 | onUpdate() | |
1399 | recomplete() | |
1400 | redrawText() | |
1401 | end | |
1402 | ||
1403 | elseif param == keys.enter then | |
1404 | -- Enter | |
1405 | if not bMenu and not bReadOnly then | |
1406 | deleteMarked() | |
1407 | ||
1408 | -- Newline | |
1409 | local sLine = tLines[y] | |
1410 | local _,tabs=string.find(sLine,"^[\t]+") | |
1411 | if not tabs then | |
1412 | tabs=0 | |
1413 | end | |
1414 | tLines[y] = string.sub(sLine,1,x-1) | |
1415 | table.insert( tLines, y+1, string.rep('\t',tabs)..string.sub(sLine,x) ) | |
1416 | onUpdate() | |
1417 | setCursorMark( tabs + 1, y + 1 ) | |
1418 | redrawText() | |
1419 | ||
1420 | elseif bMenu then | |
1421 | -- Menu selection | |
1422 | doMenuItem( nMenuItem ) | |
1423 | ||
1424 | end | |
1425 | ||
1426 | elseif (param == keys.leftCtrl or param == keys.rightCtrl) and not param2 then | |
1427 | -- Menu toggle | |
1428 | bMenu = not bMenu | |
1429 | if bMenu then | |
1430 | term.setCursorBlink( false ) | |
1431 | else | |
1432 | term.setCursorBlink( true ) | |
1433 | end | |
1434 | redrawMenu() | |
1435 | ||
1436 | elseif (param == keys.rightAlt) then | |
1437 | if bMenu then | |
1438 | bMenu = false | |
1439 | term.setCursorBlink( true ) | |
1440 | redrawMenu() | |
1441 | end | |
1442 | ||
1443 | elseif param == 42 then | |
1444 | holdingShift = true | |
1445 | end | |
1446 | ||
1447 | elseif sEvent == 'key_up' then | |
1448 | if param == 42 then | |
1449 | holdingShift = false | |
1450 | end | |
1451 | ||
1452 | elseif sEvent == "char" then | |
1453 | if not bMenu and not bReadOnly then | |
1454 | deleteMarked() | |
1455 | ||
1456 | -- Input text | |
1457 | local sLine = tLines[y] | |
1458 | tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x) | |
1459 | onUpdate() | |
1460 | setCursorMark( x + 1, y ) | |
1461 | ||
1462 | elseif bMenu then | |
1463 | -- Select menu items | |
1464 | for n,sMenuItem in ipairs( tMenuItems ) do | |
1465 | if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then | |
1466 | doMenuItem( n ) | |
1467 | break | |
1468 | end | |
1469 | end | |
1470 | end | |
1471 | ||
1472 | elseif sEvent == "paste" then | |
1473 | if not bMenu and not bReadOnly then | |
1474 | -- Input text | |
1475 | --[[local sLine = tLines[y] | |
1476 | tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x) | |
1477 | onUpdate() | |
1478 | setCursorMark( x + string.len( param ), y )]] | |
1479 | ||
1480 | --[[deleteMarked() | |
1481 | ||
1482 | local newX, newY = x, y | |
1483 | ||
1484 | for i = 1, #param do | |
1485 | local char = param:sub(i, i) | |
1486 | ||
1487 | if char == '\n' then | |
1488 | local rest = tLines[newY]:sub(newX) | |
1489 | tLines[newY] = tLines[newY]:sub(1, newX - 1) | |
1490 | ||
1491 | table.insert(tLines, rest, newY + 1) | |
1492 | ||
1493 | newY = newY + 1 | |
1494 | newX = 1 | |
1495 | else | |
1496 | local line = tLines[newY] | |
1497 | ||
1498 | line = line:sub(1, newX - 1) .. char .. line:sub(newX) | |
1499 | ||
1500 | tLines[newY] = line | |
1501 | newX = newX + 1 | |
1502 | end | |
1503 | end | |
1504 | ||
1505 | onUpdate() | |
1506 | setCursorMark(newX, newY)]] | |
1507 | ||
1508 | local oldvClipboard = vClipboard | |
1509 | vClipboard = param | |
1510 | tMenuFuncs.VPaste() | |
1511 | redrawMenu() | |
1512 | vClipboard = oldvClipboard | |
1513 | end | |
1514 | ||
1515 | elseif sEvent == "mouse_click" then | |
1516 | local cursorSet = setCursorMark | |
1517 | ||
1518 | if holdingShift then | |
1519 | cursorSet = setCursor | |
1520 | end | |
1521 | ||
1522 | if not bMenu then | |
1523 | if param == 1 then | |
1524 | -- Left click | |
1525 | local cx,cy = param2, param3 | |
1526 | if cy < h then | |
1527 | local tx, ty = cx + scrollX, cy + scrollY | |
1528 | local newX, newY | |
1529 | ||
1530 | if ty <= #tLines then | |
1531 | newY = math.min(#tLines, math.max(ty, 1)) | |
1532 | tx = cxToPos(ty, tx) | |
1533 | newX = math.min(tLines[newY]:len() + 1, math.max(1, tx)) | |
1534 | else | |
1535 | newY = #tLines | |
1536 | newX = #tLines[newY] + 1 | |
1537 | end | |
1538 | ||
1539 | cursorSet(newX, newY) | |
1540 | end | |
1541 | end | |
1542 | end | |
1543 | ||
1544 | elseif sEvent == "mouse_drag" then | |
1545 | if not bMenu then | |
1546 | if param == 1 then | |
1547 | -- Left click | |
1548 | local cx,cy = param2, param3 | |
1549 | if cy < h then | |
1550 | local tx, ty = cx + scrollX, cy + scrollY | |
1551 | local newX, newY | |
1552 | ||
1553 | if ty <= #tLines then | |
1554 | newY = math.min(#tLines, math.max(ty, 1)) | |
1555 | tx = cxToPos(ty, tx) | |
1556 | newX = math.min(tLines[newY]:len() + 1, math.max(1, tx)) | |
1557 | else | |
1558 | newY = #tLines | |
1559 | newX = #tLines[newY] + 1 | |
1560 | end | |
1561 | ||
1562 | setCursor(newX, newY) | |
1563 | end | |
1564 | end | |
1565 | end | |
1566 | ||
1567 | elseif sEvent == "mouse_scroll" then | |
1568 | if not bMenu then | |
1569 | if param == -1 then | |
1570 | -- Scroll up | |
1571 | if scrollY > 0 then | |
1572 | -- Move cursor up | |
1573 | scrollY = scrollY - 1 | |
1574 | redrawText() | |
1575 | end | |
1576 | ||
1577 | elseif param == 1 then | |
1578 | -- Scroll down | |
1579 | local nMaxScroll = #tLines - (h-1) | |
1580 | if scrollY < nMaxScroll then | |
1581 | -- Move cursor down | |
1582 | scrollY = scrollY + 1 | |
1583 | redrawText() | |
1584 | end | |
1585 | ||
1586 | end | |
1587 | end | |
1588 | ||
1589 | elseif sEvent == "term_resize" then | |
1590 | w,h = term.getSize() | |
1591 | setCursor( x, y ) | |
1592 | redrawMenu() | |
1593 | redrawText() | |
1594 | ||
1595 | end | |
1596 | end | |
1597 | ||
1598 | -- Cleanup | |
1599 | term.clear() | |
1600 | term.setCursorBlink( false ) | |
1601 | term.setCursorPos( 1, 1 ) |