View in color | License | Download script | History | Other scripts by: gabriele |
30-Apr 16:00 UTC
[0.061] 28.275k
[0.061] 28.275k
ropus.rREBOL [
Title: "Simple file manager"
Date: 12-Dec-1999
Purpose: "A simple file manager"
Version: 2.0.1.3
File: %ropus.r
Author: "Gabriele Santilli"
History: [
12-Dec-1999 1.0.1.1 "First working version"
12-Dec-1999 1.0.1.2 "Now get-input keeps a buffer, and it's way smarter"
12-Dec-1999 2.0.1.3 "Finished ask-user; posting to the list"
]
library: [
level: 'intermediate
platform: none
type: none
domain: 'file-handling
tested-under: none
support: none
license: none
see-also: none
]
]
system/options/quiet: true
; -- text interface (requires REBOL 2.2)
restring: func [b] [make string! reduce b]
control-sequences: make object! [
csi: "^(1B)["
sequence: func [
"Creates a sequence"
s [block!]
] [
restring bind s 'self
]
left: func [
"Moves the cursor n chars left"
n [integer!]
] [
sequence [csi n "D"]
]
right: func [
"Moves the cursor n chars right"
n [integer!]
] [
sequence [csi n "C"]
]
up: func [
"Moves the cursor n rows up"
n [integer!]
] [
sequence [csi n "A"]
]
down: func [
"Moves the cursor n rows down"
n [integer!]
] [
sequence [csi n "B"]
]
move-to: func [
"Moves the cursor to the given position"
row [integer!]
column [integer!]
] [
sequence [csi row ";" column "H"]
]
home: sequence [csi "H"] ; goto (1, 1)
delete: func [
"Deletes n chars to the right"
n [integer!]
] [
sequence [csi n "P"]
]
insert: func [
"Inserts n spaces"
n [integer!]
] [
sequence [csi n "@"]
]
cls: sequence [csi "J"] ; clear screen
clear-to-end-of-line: sequence [csi "K"]
cursor-pos: sequence [csi "6n"]
dimensions: sequence [csi "7n"]
]
control-chars: charset [#"^(00)" - #"^(1F)" #"^(7F)" - #"^(9F)"]
control-char?: func [
"Is it a control character?"
char [char!]
] [
find control-chars char
]
emit: func [
"Emit to the console"
value
] [
write/binary console:// value
]
read-con: func [
"Read from the console"
] [
read/binary/wait console://
]
input-buffer: make block! 100
get-input: func [
"Waits keyboard input and parses it"
/local char
] [
if empty? input-buffer [
parse/all read-con [
some [
"^(1B)[A" (insert tail input-buffer 'up) |
"^(1B)[B" (insert tail input-buffer 'down) |
"^(1B)[C" (insert tail input-buffer 'right) |
"^(1B)[D" (insert tail input-buffer 'left) |
"^(1B)[5~" (insert tail input-buffer 'page-up) |
"^(1B)[6~" (insert tail input-buffer 'page-down) |
"^(7F)" (insert tail input-buffer 'delete) |
"^-" (insert tail input-buffer 'tab) |
"^M" (insert tail input-buffer 'enter) |
"^(08)" (insert tail input-buffer 'backspace) |
copy char skip (insert tail input-buffer to-char char)
]
]
]
char: input-buffer/1
remove input-buffer
char
]
input-loop: func [
"Input loop"
body [block!]
/local input
] [
forever [
switch get-input body
]
]
send-sequence: func [
"Sends a sequence to the console"
seq [block!]
] [
emit control-sequences/sequence seq
]
digits: charset "1234567890"
console-get: func [
"Reads cursor position or console dimensions"
'what [word!]
/local row col
] [
send-sequence reduce [what]
parse/all read-con [
"^(1B)["
copy row some digits
";"
copy col some digits
"R" end
]
reduce [to-integer row to-integer col]
]
get-cursor: func [] [console-get cursor-pos]
get-dimensions: func [] [console-get dimensions]
footer: restring [
"ROpus " system/script/header/version/3 "." system/script/header/version/4
" (release " system/script/header/version/1
pick [" ALPHA" " BETA" ""] system/script/header/version/2 + 1 ")"
" --- ©1999 Gabriele Santilli --- Press H for help"
]
redraw: func [
"Redraws the screen"
/local width height
] [
set [height width] get-dimensions
send-sequence [
cls
move-to height 1
copy/part footer width
home
]
lister1/set-rect 1 1 to-integer width / 2 height - 1
lister2/set-rect (to-integer width / 2) + 1 1 to-integer width / 2 height - 1
lister1/redraw
lister2/redraw
]
show-text: func [
"Shows a text on screen (used by view-text)"
lines [block!] "Block of lines"
sk [integer!] "Horizontal scroll"
rows [integer!] "Numbers of rows to show"
margin [integer!] "Maximum line length"
/local line x
] [
rows: min rows length? lines
line: make paren! [copy/part skip lines/1 sk margin]
x: margin + 2
for y 2 rows + 1 1 [
send-sequence [
move-to y 2
line clear-to-end-of-line
move-to y x "|"
]
lines: next lines
]
]
show-box: func [
"Shows a box on screen"
x [integer!]
y [integer!]
lines [block!]
] [
for y y (y + length? lines) - 1 1 [
send-sequence [
move-to y x
lines/1
]
lines: next lines
]
]
message: func [
"Shows a message"
msg [string! block!] "Line or block of lines"
/confirm "Ask confirmation to the user"
/local scrw scrh boxw boxh boxx boxy box border blank res
] [
set [scrh scrw] get-dimensions
if string? msg [msg: reduce [msg]]
boxh: length? msg
boxw: 13
foreach line msg [if boxw < length? line [boxw: length? line]]
box: make block! 100
border: make string! 100
blank: make string! 100
insert insert/dup insert border "+" "-" boxw "+"
insert/dup blank " " boxw
boxx: to-integer ((scrw - boxw) / 2) - 1
boxy: to-integer ((scrh - boxh) / 2) - 1
insert box border
foreach line msg [
insert tail box restring [
"|" head change copy blank copy/part line boxw "|"
]
]
insert tail box border
res: either confirm [
insert tail box restring [
"| [Y]es" head insert/dup copy "" " " (boxw - 11) "[N]o |"
]
insert tail box border
boxy: boxy - 1
show-box boxx boxy box
select [#"Y" true #"N" false] get-input
] [
show-box boxx boxy box
get-input
]
redraw
res
]
lister: make object! [
x: y: w: h: 0
border: make string! 100
blank: make string! 100
set-rect: func [
xx yy ww hh
] [
set [x y w h] reduce [xx yy ww hh]
clear border
clear blank
insert insert/dup insert border "+" "-" w - 2 "+"
insert insert/dup insert blank "| " " " w - 3 "|"
]
list: make block! 0
current: 1
redraw: func [
"Disegna il lister"
/local line row
] [
send-sequence [
move-to y x
border
move-to y + h - 1 x
border
]
row: y + 1
foreach element copy/part list h - 2 [
line: head
change
next next copy blank
copy/part form element w - 3
send-sequence [
move-to row x
line
]
row: row + 1
]
for row row y + h - 2 1 [
send-sequence [
move-to row x
blank
]
]
draw-pointer
]
draw-pointer: func [] [
send-sequence [
move-to y + current x + 1
">"
]
]
clear-pointer: func [] [
send-sequence [
move-to y + current x + 1
" "
]
]
down: func [] [
if current < length? list [
clear-pointer
either current < (h - 2) [
current: current + 1
draw-pointer
] [
list: next list
redraw
]
]
]
up: func [] [
either current > 1 [
clear-pointer
current: current - 1
draw-pointer
] [
if not head? list [
list: back list
redraw
]
]
]
get-current: func [] [
pick list current
]
]
lister1: make lister []
lister2: make lister []
ask-user: func [
"Asks a question to the user"
question [string!]
/local scrw scrh buffer bc cpos maxlen xpos key refresh
] [
bc: buffer: make string! 256
set [scrh scrw] get-dimensions
send-sequence [
move-to scrh 1
question
clear-to-end-of-line
]
xpos: 1 + length? question
cpos: xpos
maxlen: scrw - xpos
refresh: make paren! [
send-sequence [
move-to scrh xpos
copy/part buffer maxlen
clear-to-end-of-line
move-to scrh cpos
]
]
while ['enter <> key: get-input] [
either all [char? key not control-char? key] [
bc: insert bc key
either cpos >= scrw [
buffer: next buffer
refresh
] [
cpos: cpos + 1
either not tail? bc [
refresh
] [
emit key
]
]
] [
switch key [
left [
either all [cpos = xpos not head? buffer] [
bc: buffer: back buffer
refresh
] [
if cpos > xpos [
cpos: cpos - 1
bc: back bc
send-sequence [left 1]
]
]
]
right [
either all [cpos = scrw maxlen < length? buffer] [
buffer: next buffer
bc: next bc
refresh
] [
if all [cpos < scrw not tail? bc] [
cpos: cpos + 1
bc: next bc
send-sequence [right 1]
]
]
]
delete [
if not tail? bc [
remove bc
refresh
]
]
backspace [
either all [cpos = xpos not head? buffer] [
bc: buffer: back buffer
remove bc
refresh
] [
if cpos > xpos [
cpos: cpos - 1
bc: back bc
remove bc
refresh
]
]
]
]
]
]
head buffer
]
; -- file manager
change-active-dir: func [
"Changes the active directory"
dir [file! url!]
] [
source-dest/2: dir
source-dest/1/list: sort read dir
source-dest/1/current: 1
source-dest/1/redraw
]
refresh: func [
"Refreshes the listers and redraws the screen"
] [
source-dest/1/list: sort read source-dest/2
source-dest/3/list: sort read source-dest/4
redraw
]
swap: func [
"Exchange source and destination"
sd [block!]
] [
change sd reduce [sd/3 sd/4 sd/1 sd/2]
]
cases-dialect: make object! [
"Dialect for do-cases"
else-if: if: func [
condition
body [block!]
] [
system/words/if condition [
do body
true
]
]
else: :do
]
do-cases: func [
"Executes the case whose condition is true"
; example for cases:
; if cond1 [code1] else-if cond2 [code2] ... else [default]
cases [block!]
] [
any bind cases in cases-dialect 'self
]
form-error: func [
"Forms an error message"
error [error!]
/local id type
] [
error: disarm error
id: error/id
type: error/type
reduce [
"*** Error message"
reform ["*** Type:" system/error/:type/type]
reform ["*** Why:" reform bind system/error/:type/:id in error 'self]
reform ["*** Near:" trim/lines mold error/near]
]
]
execute-script: func [
"Executes the given script"
script [file! url!]
/local result id type
] [
send-sequence [cls]
print "Trying to execute the script..."
either error? result: try [do script] [
foreach line form-error result [
print line
]
] [
if value? 'result [print ["Result:" mold result]]
]
print "Press any key to continue..."
get-input
refresh
]
text?: func [
"Is it a text file?"
file [file! url!]
/local freq sum
] [
file: read/binary/part file 512
freq: array/initial 256 0
foreach byte file [
byte: byte + 1
poke freq byte freq/:byte + 1
]
sum: 0
for i 32 126 1 [sum: sum + freq/:i]
sum > ((4 * length? file) / 5)
]
view-text: func [
"Text file viewer --- slow but nice ;-)"
textfile [file! url!]
/local scrh scrw border footer sk refresh maxskip maxindex
] [
set [scrh scrw] get-dimensions
border: make string! 100
insert insert/dup insert border "+" "-" scrw - 2 "+"
footer: restring [
"Q to quit --- " textfile
]
send-sequence [
cls
border CRLF
down scrh - 3
border CRLF
copy/part footer scrw
]
for i 2 scrh - 2 1 [
send-sequence [
move-to i 1 "|"
right scrw - 2 "|" CRLF
]
]
textfile: parse/all detab/size read textfile 4 "^/"
maxskip: 0
foreach line textfile [if maxskip < length? line [maxskip: length? line]]
maxskip: max 0 maxskip - (scrw - 2)
maxindex: (4 + length? textfile) - scrh
show-text textfile 0 scrh - 3 scrw - 2
sk: 0
refresh: make paren! [
show-text textfile sk scrh - 3 scrw - 2
]
input-loop [
#"Q" [break]
up [
if not head? textfile [
textfile: back textfile
refresh
]
]
down [
if maxindex > index? textfile [
textfile: next textfile
refresh
]
]
left [
if sk > 0 [
sk: sk - 1
refresh
]
]
right [
if sk < maxskip [
sk: sk + 1
refresh
]
]
page-up [
if not head? textfile [
textfile: skip textfile negate scrh - 4
refresh
]
]
page-down [
if maxindex > index? textfile [
textfile: skip textfile min scrh - 4 maxindex - index? textfile
refresh
]
]
]
redraw
]
show-info: func [
"Shows file info"
file [file! url!]
/local name maxlen info
] [
maxlen: to-integer (pick get-dimensions 2) / 2
name: form file
if maxlen < length? name [
name: restring ["..." skip tail name negate (maxlen - 3)]
]
info: info? file
message reduce [
restring ["Informations on " name]
restring ["Size: " info/size]
restring ["Last modification: " info/date]
]
]
source-dest: reduce [
lister1 system/script/path
lister2 system/script/path
]
refresh
show-message-on-error: func [
"Shows a message in case of error"
code [block!]
/local error
] [
if error? set/any 'error try code [
message form-error error
]
]
; main input loop
input-loop [
#"Q" [
send-sequence [cls]
break
]
up [
source-dest/1/up
]
down [
source-dest/1/down
]
tab [
swap source-dest
]
enter [
show-message-on-error [
file: join source-dest/2 source-dest/1/get-current
do-cases [
if dir? file [
change-active-dir file
]
else-if script? file [
execute-script file
]
else-if text? file [
view-text file
]
else [
show-info file
]
]
]
]
#"P" [
show-message-on-error [
change-active-dir first split-path source-dest/2
]
]
delete [
show-message-on-error [
file: source-dest/1/get-current
if message/confirm reduce [
"Please confirm deletion"
reform ["of" file]
] [
delete join source-dest/2 file
refresh
]
]
]
#"V" [
show-message-on-error [
view-text join source-dest/2 source-dest/1/get-current
]
]
#"C" [
show-message-on-error [
use [source dest file] [
source: join source-dest/2 file: source-dest/1/get-current
dest: join source-dest/4 file
write/binary dest read/binary source
]
refresh
]
]
#"M" [
show-message-on-error [
use [source dest file] [
source: join source-dest/2 file: source-dest/1/get-current
dest: join source-dest/4 file
write/binary dest read/binary source
delete source
]
refresh
]
]
#"R" [refresh]
#"G" [ ; goto
show-message-on-error [
file: load ask-user "Go to dir: "
if any [file? file url? file] [
if dir? file [
change-active-dir file
]
]
]
use [w h] [
set [h w] get-dimensions
send-sequence [
move-to h 1
copy/part footer w
]
]
]
#"H" [
message [
" KEY | FUNCTION"
"-----+------------------------------------"
" Q | Quit ROpus"
" H | Show this help"
" G | Change directory (accepts URLs too)"
" R | Refresh the screen"
" M | Move file"
" C | Copy file"
" V | View text file"
" DEL | Delete file"
" P | Go to parent directory"
"ENTER| Perform action based on file type"
" TAB | Exchange source/destination lister"
"ARROW| Select file"
]
]
] |