rtfm-filemanager 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rtfm.launch +28 -0
- data/bin/rtfm +1240 -0
- metadata +49 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e53ed89ba2707d8182a3436b5d0141d145a715a4a51db721fd9b84cfb97d7c96
|
|
4
|
+
data.tar.gz: f4fe71f58c4a4c2e46b40034af4b78cb9afc45f9820f4c6de46f70ebf7591f15
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4aaf7f5b9a0ec542f20a1bca46256c1cff8d24cdb55418f7dad7a0a259611a3f4146e70c01cdd5381bd52ba2de39a3e46e8834bed9c589f57c6138dc4ebcb0bd
|
|
7
|
+
data.tar.gz: 4e01bc468ff57bdab8ce085b7c974b6c53588cae2114200970ac9fe82f8921e090813afb919d7b311e15ef6275236cda81188e83cb28bad4ecd1be6a6ea93be6
|
data/.rtfm.launch
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# This function starts RTFM and will cd to the exit dir
|
|
2
|
+
#
|
|
3
|
+
# Add this line to your .bashrc or .zshrc to make RTFM exit to the
|
|
4
|
+
# current directory by launching the file manager via r in the terminal:
|
|
5
|
+
# source ~/.rtfm.launch
|
|
6
|
+
# ... and place the file .rtfm.launch in your home directory.
|
|
7
|
+
# With this, you can jump around in your directory structure via RTFM, exit to
|
|
8
|
+
# the desired directory, do work in the terminal and go back into RTFM via r.
|
|
9
|
+
|
|
10
|
+
function r {
|
|
11
|
+
f=$(mktemp)
|
|
12
|
+
(
|
|
13
|
+
set +e
|
|
14
|
+
rtfm "$f"
|
|
15
|
+
code=$?
|
|
16
|
+
if [ "$code" != 0 ]; then
|
|
17
|
+
rm -f "$f"
|
|
18
|
+
exit "$code"
|
|
19
|
+
fi
|
|
20
|
+
)
|
|
21
|
+
code=$?
|
|
22
|
+
if [ "$code" != 0 ]; then
|
|
23
|
+
return "$code"
|
|
24
|
+
fi
|
|
25
|
+
d=$(<"$f")
|
|
26
|
+
rm -f "$f"
|
|
27
|
+
cd "$d"
|
|
28
|
+
}
|
data/bin/rtfm
ADDED
|
@@ -0,0 +1,1240 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
|
|
4
|
+
# SCRIPT INFO
|
|
5
|
+
# Name: RTFM - Ruby Terminal File Manager
|
|
6
|
+
# Language: Pure Ruby, best viewed in VIM
|
|
7
|
+
# Author: Geir Isene <g@isene.com>
|
|
8
|
+
# Web_site: http://isene.com/
|
|
9
|
+
# Github: https://github.com/isene/RTFM
|
|
10
|
+
# License: I release all copyright claims. This code is in the public domain.
|
|
11
|
+
# Permission is granted to use, copy modify, distribute, and sell
|
|
12
|
+
# this software for any purpose. I make no guarantee about the
|
|
13
|
+
# suitability of this software for any purpose and I am not liable
|
|
14
|
+
# for any damages resulting from its use. Further, I am under no
|
|
15
|
+
# obligation to maintain or extend this software. It is provided
|
|
16
|
+
# on an 'as is' basis without any expressed or implied warranty.
|
|
17
|
+
|
|
18
|
+
# PRELIMINARIES
|
|
19
|
+
@help = <<HELPTEXT
|
|
20
|
+
RTFM - Ruby Terminal File Manager (https://github.com/isene/RTFM)
|
|
21
|
+
|
|
22
|
+
BASIC KEYS
|
|
23
|
+
? = Show this help text
|
|
24
|
+
r = Refresh RTFM (recreates all windows. Use on terminal resize or when there is garbage somewhere)
|
|
25
|
+
R = Reload configuration (~/.rtfm.conf)
|
|
26
|
+
W = Write parameters to ~/.rtfm.conf (@lsall, @lslong, @border, @width, @preview, @tagged, @marks)
|
|
27
|
+
q = Quit
|
|
28
|
+
Q = QUIT (without writing changes to the config file)
|
|
29
|
+
|
|
30
|
+
MOTION
|
|
31
|
+
DOWN = Go one item down in left pane (rounds to top)
|
|
32
|
+
UP = Go one item up in left pane (rounds to bottom)
|
|
33
|
+
LEFT = Go up one directory level
|
|
34
|
+
RIGHT = Enter directory or open file (using run-mailcap or xdg-open)
|
|
35
|
+
Use the key 'x' to force open using xdg-open (or run-mailcap) - used for opening html files
|
|
36
|
+
in a browser rather than editing the file in your text editor
|
|
37
|
+
PgDown = Go one page down in left pane
|
|
38
|
+
PgUp = Go one page up in left pane
|
|
39
|
+
END = Go to last item in left pane
|
|
40
|
+
HOME = Go to first item in left pane
|
|
41
|
+
|
|
42
|
+
JUMPING AND MARKS
|
|
43
|
+
m = Mark current dir (persistent). Next letter is the name of the mark [a-zA-Z']
|
|
44
|
+
The special mark "'" jumps to the last directory (makes toggling dirs easy)
|
|
45
|
+
Press '-' and a letter to delete that mark
|
|
46
|
+
M = Show marked items in right pane
|
|
47
|
+
' = Jump to mark (next letter is the name of the mark [a-zA-Z'])
|
|
48
|
+
h = Jump to Home directory
|
|
49
|
+
f = Follow symlink to the directory where the target resides
|
|
50
|
+
L = Start 'locate' search for files, then use '#' to jump to desired line/directory
|
|
51
|
+
|
|
52
|
+
TAGGING
|
|
53
|
+
t = Tag item (toggles)
|
|
54
|
+
Ctrl-t = Add items matching a pattern to list of tagged items (Ctrl-t and then . will tag all items)
|
|
55
|
+
T = Show currently tagged items in right pane
|
|
56
|
+
u = Untag all tagged items
|
|
57
|
+
|
|
58
|
+
MANIPULATE ITEMS
|
|
59
|
+
p = Put (copy) tagged items here
|
|
60
|
+
P = PUT (move) tagged items here
|
|
61
|
+
s = Create symlink to tagged items here
|
|
62
|
+
d = Delete selected item and tagged items. Press 'd' to confirm
|
|
63
|
+
c = Change/rename selected (adds command to bottom window)
|
|
64
|
+
|
|
65
|
+
DIRECTORY VIEWS
|
|
66
|
+
a = Show all (also hidden) items
|
|
67
|
+
l = Show long info per item (show item attributes)
|
|
68
|
+
o = Change the order/sorting of directories (circular toggle)
|
|
69
|
+
i = Invert/reverse the sorting
|
|
70
|
+
O = Show the Ordering in the bottom window (the full ls command)
|
|
71
|
+
G = Show git status for current directory
|
|
72
|
+
H = Do a cryptographic hash of the current directory with subdirs
|
|
73
|
+
If a previous hash was made, compare and report if there has been any change
|
|
74
|
+
|
|
75
|
+
RIGHT PANE
|
|
76
|
+
ENTER = Refresh the right pane
|
|
77
|
+
TAB = Next page of the preview (if doc long and ∇ in the bottom right)
|
|
78
|
+
S-TAB = Previous page (if you have moved down the document first - ∆ in the top right)
|
|
79
|
+
w = Change the width of the left/right panes (left pane ⇒ ⅓ ⇒ ¼ ⇒ ⅕ ⇒ ⅙ ⇒ ½ ⇒ ⅓)
|
|
80
|
+
- = Toggle preview in right pane (turn it off for faster traversing of directories)
|
|
81
|
+
|
|
82
|
+
ADDITINAL COMMANDS
|
|
83
|
+
/ = Enter search string in bottom window to highlight matching items
|
|
84
|
+
: = Enter "command mode" in bottom window
|
|
85
|
+
; = Show command history in right pane
|
|
86
|
+
y = Copy path of selected item to primary selection (for pasting with middle mouse button)
|
|
87
|
+
Y = Copy path of selected item to clipboard
|
|
88
|
+
|
|
89
|
+
COPYRIGHT: Geir Isene, 2020-1. No rights reserved. See http://isene.com for more.
|
|
90
|
+
HELPTEXT
|
|
91
|
+
begin # BASIC SETUP
|
|
92
|
+
require 'fileutils'
|
|
93
|
+
require 'io/console'
|
|
94
|
+
require 'date'
|
|
95
|
+
require 'curses'
|
|
96
|
+
include Curses
|
|
97
|
+
|
|
98
|
+
def cmd?(command)
|
|
99
|
+
system("which #{command} > /dev/null 2>&1")
|
|
100
|
+
end
|
|
101
|
+
if cmd?('/usr/lib/w3m/w3mimgdisplay')
|
|
102
|
+
@w3mimgdisplay = "/usr/lib/w3m/w3mimgdisplay"
|
|
103
|
+
@showimage = true
|
|
104
|
+
else
|
|
105
|
+
@showimage = false
|
|
106
|
+
end
|
|
107
|
+
@showimage = false unless (cmd?('xwininfo') and cmd?('xdotool'))
|
|
108
|
+
|
|
109
|
+
STDIN.set_encoding(Encoding::UTF_8) # Set encoding for STDIN
|
|
110
|
+
LScolors = `echo $LS_COLORS` # Import LS_COLORS
|
|
111
|
+
|
|
112
|
+
## Curses setup
|
|
113
|
+
Curses.init_screen
|
|
114
|
+
Curses.start_color
|
|
115
|
+
Curses.curs_set(0)
|
|
116
|
+
Curses.noecho
|
|
117
|
+
Curses.cbreak
|
|
118
|
+
Curses.stdscr.keypad = true
|
|
119
|
+
|
|
120
|
+
# INITIALIZE VARIABLES
|
|
121
|
+
## These can be set by user in .rtfm.conf
|
|
122
|
+
@lsbase = "--group-directories-first" # Basic ls setup
|
|
123
|
+
@lslong = false # Set short form ls (toggled by pressing "l")
|
|
124
|
+
@lsall = "" # Set "ls -a" to false (toggled by pressing "a" - sets it to "-a")
|
|
125
|
+
@lsorder = "" # Change the order/sorting by pressing 'o' (circular toggle)
|
|
126
|
+
@lsinvert = "" # Set to "-r" to reverse/invert sorting order
|
|
127
|
+
@lsuser = "" # Set this variable in .rtfm.conf to any 'ls' switch you want to customize directory listings
|
|
128
|
+
@width = 3 # Set width of the left pane to the default ⅓ of the terminal width
|
|
129
|
+
@history = [] # Initialize the command line history array
|
|
130
|
+
@border = false
|
|
131
|
+
@preview = true
|
|
132
|
+
@runmailcap = false # Set to 'true' in .rtfm.conf if you want to use run-mailcap instead of xdg-open
|
|
133
|
+
## These are automatically written on exit
|
|
134
|
+
@marks = {} # Initialize (book)marks hash
|
|
135
|
+
@hash = {} # Initialize the sha directory hashing
|
|
136
|
+
@tagged = [] # Initialize the tagged array - for collecting all tagged items
|
|
137
|
+
## These should not be set by user in .rtfm.conf
|
|
138
|
+
@directory = {} # Initialize the directory hash for remembering directories visited
|
|
139
|
+
@searched = "" # Initialize the active searched for items
|
|
140
|
+
@index = 0 # Set chosen item to first on startup
|
|
141
|
+
@marks["'"] = Dir.pwd
|
|
142
|
+
## File type recognizers
|
|
143
|
+
@imagefile = /\.jpg$|\.JPG$|\.jpeg$|\.png$|\.bmp$|\.gif$|\.tif$|\.tiff$/
|
|
144
|
+
@pptfile = /\.ppt$/
|
|
145
|
+
@xlsfile = /\.xls$/
|
|
146
|
+
@docfile = /\.doc$/
|
|
147
|
+
@docxfile = /\.docx$/
|
|
148
|
+
@xlsxfile = /\.xlsx$/
|
|
149
|
+
@pptxfile = /\.pptx$/
|
|
150
|
+
@oolofile = /\.odt$|\.odc$|\.odp$|\.odg$/
|
|
151
|
+
@pdffile = /\.pdf$|\.ps$/
|
|
152
|
+
## Get variables from config file (written back to .rtf.conf upon exit via 'q')
|
|
153
|
+
if File.exist?(Dir.home+'/.rtfm.conf')
|
|
154
|
+
load(Dir.home+'/.rtfm.conf')
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
class Curses::Window # CLASS EXTENSION
|
|
158
|
+
attr_accessor :fg, :bg, :attr, :text, :update, :pager, :pager_more, :pager_cmd, :locate, :nohistory
|
|
159
|
+
# General extensions (see https://github.com/isene/Ruby-Curses-Class-Extension)
|
|
160
|
+
def clr
|
|
161
|
+
self.setpos(0, 0)
|
|
162
|
+
self.maxy.times {self.deleteln()}
|
|
163
|
+
self.refresh
|
|
164
|
+
self.setpos(0, 0)
|
|
165
|
+
end
|
|
166
|
+
def fill # Fill window with color as set by :bg
|
|
167
|
+
self.setpos(0, 0)
|
|
168
|
+
self.bg = 0 if self.bg == nil
|
|
169
|
+
self.fg = 255 if self.fg == nil
|
|
170
|
+
init_pair(self.fg, self.fg, self.bg)
|
|
171
|
+
blank = " " * self.maxx
|
|
172
|
+
self.maxy.times {self.attron(color_pair(self.fg)) {self << blank}}
|
|
173
|
+
self.refresh
|
|
174
|
+
self.setpos(0, 0)
|
|
175
|
+
end
|
|
176
|
+
def write # Write context of :text to window with attributes :attr
|
|
177
|
+
self.bg = 0 if self.bg == nil
|
|
178
|
+
self.fg = 255 if self.fg == nil
|
|
179
|
+
init_pair(self.fg, self.fg, self.bg)
|
|
180
|
+
self.attr = 0 if self.attr == nil
|
|
181
|
+
self.attron(color_pair(self.fg) | self.attr) { self << self.text }
|
|
182
|
+
self.refresh
|
|
183
|
+
end
|
|
184
|
+
# RTFM specific extensions
|
|
185
|
+
end
|
|
186
|
+
# GENERIC FUNCTIONS
|
|
187
|
+
def get_ls_color(type) # GET THE COLOR FOR THE FILETYPE FROM IMPORTED LS_COLORS
|
|
188
|
+
bold = 0
|
|
189
|
+
begin
|
|
190
|
+
color = LScolors.match(/#{type}=\d*;\d*;(\d*)/)[1]
|
|
191
|
+
bold = 1 if LScolors.match(/#{type}=\d*;\d*;\d*;1/)
|
|
192
|
+
rescue
|
|
193
|
+
color = 7 # Default color
|
|
194
|
+
end
|
|
195
|
+
return color.to_i, bold
|
|
196
|
+
end
|
|
197
|
+
def color_parse(input) # PARSE ANSI COLOR SEQUENCES
|
|
198
|
+
input.gsub!( /\e\[\d;38;5;(\d+)m/, '%-%\1%-%')
|
|
199
|
+
input.gsub!( /\e\[38;5;(\d+)m/, '%-%\1%-%')
|
|
200
|
+
input.gsub!( /\e\[0m/, "\t")
|
|
201
|
+
color_array = input.split("%-%")
|
|
202
|
+
color_array = color_array.drop(1)
|
|
203
|
+
output = color_array.each_slice(2).to_a
|
|
204
|
+
return output
|
|
205
|
+
end
|
|
206
|
+
def getchr # PROCESS KEY PRESSES
|
|
207
|
+
# Note: Curses.getch blanks out @w_t
|
|
208
|
+
# @w_l.getch makes Curses::KEY_DOWN etc not work
|
|
209
|
+
# Therefore resorting to the generic method
|
|
210
|
+
c = STDIN.getch(min: 0, time: 5)
|
|
211
|
+
case c
|
|
212
|
+
when "\e" # ANSI escape sequences
|
|
213
|
+
case $stdin.getc
|
|
214
|
+
when '[' # CSI
|
|
215
|
+
case $stdin.getc
|
|
216
|
+
when 'A' then chr = "UP"
|
|
217
|
+
when 'B' then chr = "DOWN"
|
|
218
|
+
when 'C' then chr = "RIGHT"
|
|
219
|
+
when 'D' then chr = "LEFT"
|
|
220
|
+
when 'Z' then chr = "S-TAB"
|
|
221
|
+
when '2' then chr = "INS" ; STDIN.getc
|
|
222
|
+
when '3' then chr = "DEL" ; STDIN.getc
|
|
223
|
+
when '5' then chr = "PgUP" ; STDIN.getc
|
|
224
|
+
when '6' then chr = "PgDOWN" ; STDIN.getc
|
|
225
|
+
when '7' then chr = "HOME" ; STDIN.getc
|
|
226
|
+
when '8' then chr = "END" ; STDIN.getc
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
when "", "" then chr = "BACK"
|
|
230
|
+
when "" then chr = "WBACK"
|
|
231
|
+
when "" then chr = "LDEL"
|
|
232
|
+
when "" then chr = "C-T"
|
|
233
|
+
when "\r" then chr = "ENTER"
|
|
234
|
+
when "\t" then chr = "TAB"
|
|
235
|
+
when /./ then chr = c
|
|
236
|
+
end
|
|
237
|
+
return chr
|
|
238
|
+
end
|
|
239
|
+
def main_getkey # GET KEY FROM USER
|
|
240
|
+
dir = Dir.pwd
|
|
241
|
+
chr = getchr
|
|
242
|
+
case chr
|
|
243
|
+
# BASIC KEYS
|
|
244
|
+
when '?' # Show helptext in right window
|
|
245
|
+
w_r_info(@help)
|
|
246
|
+
@w_b.update = true
|
|
247
|
+
when 'r' # Refresh all windows
|
|
248
|
+
@break = true
|
|
249
|
+
when 'R' # Reload .rtfm.conf
|
|
250
|
+
if File.exist?(Dir.home+'/.rtfm.conf')
|
|
251
|
+
load(Dir.home+'/.rtfm.conf')
|
|
252
|
+
end
|
|
253
|
+
w_b_info(" Config reloaded")
|
|
254
|
+
when 'W' # Write all parameters to .rtfm.conf
|
|
255
|
+
@write_conf_all = true
|
|
256
|
+
conf_write
|
|
257
|
+
@w_b.update = true
|
|
258
|
+
when 'q' # Exit
|
|
259
|
+
@write_conf = true
|
|
260
|
+
exit 0
|
|
261
|
+
when 'Q' # Exit without writing to .rtfm.conf
|
|
262
|
+
system("printf \"\033]0;#{Dir.pwd}\007\"")
|
|
263
|
+
@write_conf = false
|
|
264
|
+
exit 0
|
|
265
|
+
# MOTION
|
|
266
|
+
when 'DOWN'
|
|
267
|
+
var_resets
|
|
268
|
+
@index = @index >= @max_index ? @min_index : @index + 1
|
|
269
|
+
@w_r.update = true
|
|
270
|
+
@w_b.update = true
|
|
271
|
+
when 'UP'
|
|
272
|
+
var_resets
|
|
273
|
+
@index = @index <= @min_index ? @max_index : @index - 1
|
|
274
|
+
@w_r.update = true
|
|
275
|
+
@w_b.update = true
|
|
276
|
+
when 'LEFT'
|
|
277
|
+
var_resets
|
|
278
|
+
cur_dir = Dir.pwd
|
|
279
|
+
@directory[Dir.pwd] = @selected # Store this directory before leaving
|
|
280
|
+
@marks["'"] = Dir.pwd
|
|
281
|
+
Dir.chdir("..")
|
|
282
|
+
@directory[Dir.pwd] = File.basename(cur_dir) unless @directory.key?(Dir.pwd)
|
|
283
|
+
@w_r.update = true
|
|
284
|
+
@w_b.update = true
|
|
285
|
+
when 'RIGHT'
|
|
286
|
+
var_resets
|
|
287
|
+
@directory[Dir.pwd] = @selected # Store this directory before leaving
|
|
288
|
+
@marks["'"] = Dir.pwd
|
|
289
|
+
open_selected()
|
|
290
|
+
@w_r.update = true
|
|
291
|
+
@w_b.update = true
|
|
292
|
+
when 'x' # Force open with file opener (used to open HTML files in browser)
|
|
293
|
+
var_resets
|
|
294
|
+
@directory[Dir.pwd] = @selected # Store this directory before leaving
|
|
295
|
+
@marks["'"] = Dir.pwd
|
|
296
|
+
open_selected(true)
|
|
297
|
+
@w_r.update = true
|
|
298
|
+
@w_b.update = true
|
|
299
|
+
when 'PgDOWN'
|
|
300
|
+
var_resets
|
|
301
|
+
@index += @w_l.maxy - 2
|
|
302
|
+
@index = @max_index if @index > @max_index
|
|
303
|
+
@w_r.update = true
|
|
304
|
+
@w_b.update = true
|
|
305
|
+
when 'PgUP'
|
|
306
|
+
var_resets
|
|
307
|
+
@index -= @w_l.maxy - 2
|
|
308
|
+
@index = @min_index if @index < @min_index
|
|
309
|
+
@w_r.update = true
|
|
310
|
+
@w_b.update = true
|
|
311
|
+
when 'END'
|
|
312
|
+
var_resets
|
|
313
|
+
@index = @max_index
|
|
314
|
+
@w_r.update = true
|
|
315
|
+
@w_b.update = true
|
|
316
|
+
when 'HOME'
|
|
317
|
+
var_resets
|
|
318
|
+
@index = @min_index
|
|
319
|
+
@w_r.update = true
|
|
320
|
+
@w_b.update = true
|
|
321
|
+
# JUMPING AND MARKS
|
|
322
|
+
when 'm' # Set mark
|
|
323
|
+
marks_info
|
|
324
|
+
m = STDIN.getc
|
|
325
|
+
if m.match(/[\w']/)
|
|
326
|
+
@marks[m] = Dir.pwd
|
|
327
|
+
elsif m == "-"
|
|
328
|
+
r = STDIN.getc
|
|
329
|
+
@marks.delete(r)
|
|
330
|
+
end
|
|
331
|
+
marks_info
|
|
332
|
+
@w_r.update = false
|
|
333
|
+
@w_b.update = true
|
|
334
|
+
when 'M' # Show marks
|
|
335
|
+
@marks = @marks.sort.to_h
|
|
336
|
+
marks_info
|
|
337
|
+
@w_r.update = false
|
|
338
|
+
@w_b.update = true
|
|
339
|
+
when "'" # Jump to mark
|
|
340
|
+
marks_info
|
|
341
|
+
m = STDIN.getc
|
|
342
|
+
if m.match(/[\w']/) and @marks[m]
|
|
343
|
+
var_resets
|
|
344
|
+
@directory[Dir.pwd] = @selected # Store this directory before leaving
|
|
345
|
+
dir_before = Dir.pwd
|
|
346
|
+
begin
|
|
347
|
+
Dir.chdir(@marks[m])
|
|
348
|
+
rescue
|
|
349
|
+
w_b_info(" No such directory")
|
|
350
|
+
end
|
|
351
|
+
@marks["'"] = dir_before
|
|
352
|
+
end
|
|
353
|
+
@w_r.update = true
|
|
354
|
+
@w_b.update = true
|
|
355
|
+
when 'h' # Go to home dir
|
|
356
|
+
var_resets
|
|
357
|
+
@directory[Dir.pwd] = @selected # Store this directory before leaving
|
|
358
|
+
@marks["'"] = Dir.pwd
|
|
359
|
+
Dir.chdir
|
|
360
|
+
@w_r.update = true
|
|
361
|
+
@w_b.update = true
|
|
362
|
+
when 'f' # Follow symlink
|
|
363
|
+
@directory[Dir.pwd] = @selected # Store this directory before leaving
|
|
364
|
+
@marks["'"] = Dir.pwd
|
|
365
|
+
if File.symlink?(@selected)
|
|
366
|
+
Dir.chdir(File.dirname(File.readlink(@selected)))
|
|
367
|
+
end
|
|
368
|
+
@w_b.update = true
|
|
369
|
+
when 'L' # Run 'locate' and let user jump to a result (by '#')
|
|
370
|
+
cmd = w_b_getstr(": ", "locate ")
|
|
371
|
+
w_b_exec(cmd)
|
|
372
|
+
@w_r.locate = true
|
|
373
|
+
@w_b.update = true
|
|
374
|
+
when '#' # Jump to the line number in list of matches to 'locate'
|
|
375
|
+
if @w_r.locate
|
|
376
|
+
jumpnr = w_b_getstr("# ", "").to_i
|
|
377
|
+
jumpline = @w_r.text.lines[jumpnr - 1]
|
|
378
|
+
jumpdir = jumpline[/\/[^\e]*/]
|
|
379
|
+
unless Dir.exist?(jumpdir)
|
|
380
|
+
@searched = File.basename(jumpdir)
|
|
381
|
+
jumpdir = File.dirname(jumpdir)
|
|
382
|
+
end
|
|
383
|
+
@directory[Dir.pwd] = @selected # Store this directory before leaving
|
|
384
|
+
@marks["'"] = Dir.pwd
|
|
385
|
+
Dir.chdir(jumpdir)
|
|
386
|
+
@w_r.pager = 0
|
|
387
|
+
end
|
|
388
|
+
@w_b.update = true
|
|
389
|
+
# TAGGING
|
|
390
|
+
when 't' # Add item to tagged list
|
|
391
|
+
item = "\"#{Dir.pwd}/#{@selected}\""
|
|
392
|
+
if @tagged.include?(item)
|
|
393
|
+
@tagged.delete(item)
|
|
394
|
+
else
|
|
395
|
+
@tagged.push(item)
|
|
396
|
+
end
|
|
397
|
+
@index += 1
|
|
398
|
+
@w_r.update = true
|
|
399
|
+
@w_b.update = true
|
|
400
|
+
when 'C-T' # Tag items matching a pettern
|
|
401
|
+
@w_b.nohistory = true
|
|
402
|
+
@tag = w_b_getstr("~ ", "")
|
|
403
|
+
@w_r.update = true
|
|
404
|
+
@w_b.update = true
|
|
405
|
+
when 'T' # Show tagged list
|
|
406
|
+
tagged_info
|
|
407
|
+
@w_r.update = false
|
|
408
|
+
@w_b.update = true
|
|
409
|
+
when 'u' # Clear tagged list
|
|
410
|
+
@tagged = []
|
|
411
|
+
tagged_info
|
|
412
|
+
@w_r.update = false
|
|
413
|
+
@w_b.update = true
|
|
414
|
+
# MANIPULATE ITEMS
|
|
415
|
+
when 'p' # Copy tagged items here
|
|
416
|
+
copy_move_link("copy")
|
|
417
|
+
@w_r.update = true
|
|
418
|
+
@w_b.update = true
|
|
419
|
+
when 'P' # Move tagged items here
|
|
420
|
+
copy_move_link("move")
|
|
421
|
+
@w_r.update = true
|
|
422
|
+
@w_b.update = true
|
|
423
|
+
when 's' # Create symlink to tagged items here
|
|
424
|
+
copy_move_link("link")
|
|
425
|
+
@w_r.update = true
|
|
426
|
+
@w_b.update = true
|
|
427
|
+
when 'd' # Delete items tagged and @selected
|
|
428
|
+
tagged_info
|
|
429
|
+
w_b_info(" Delete selected and tagged? (press 'd' again to delete)")
|
|
430
|
+
begin
|
|
431
|
+
@tagged.push("\"#{Dir.pwd}/#{@selected}\"")
|
|
432
|
+
@tagged.uniq!
|
|
433
|
+
deletes = @tagged.join(" ")
|
|
434
|
+
`rm -rf #{deletes} 2>/dev/null` if STDIN.getc == 'd'
|
|
435
|
+
items_number = @tagged.length
|
|
436
|
+
@tagged = []
|
|
437
|
+
w_b_info("Deleted #{items_number} items: #{deletes}")
|
|
438
|
+
@w_r.update = true
|
|
439
|
+
rescue StandardError => err
|
|
440
|
+
w_b_info(err.to_s)
|
|
441
|
+
end
|
|
442
|
+
when 'c' # Change/rename selected @selected
|
|
443
|
+
cmd = w_b_getstr(": ", "mv \"#{@selected}\" \"#{@selected}\"")
|
|
444
|
+
begin
|
|
445
|
+
w_b_exec(cmd + " 2>/dev/null")
|
|
446
|
+
rescue StandardError => err
|
|
447
|
+
w_b_info(err.to_s)
|
|
448
|
+
end
|
|
449
|
+
@w_r.update = true
|
|
450
|
+
# DIRECTORY VIEWS
|
|
451
|
+
when 'a' # Show all items
|
|
452
|
+
@lsall == "" ? @lsall = "-a" : @lsall = ""
|
|
453
|
+
@w_r.update = true
|
|
454
|
+
@w_b.update = true
|
|
455
|
+
when 'l' # Show long info for all items
|
|
456
|
+
@lslong = !@lslong
|
|
457
|
+
@w_r.update = true
|
|
458
|
+
@w_b.update = true
|
|
459
|
+
when 'o' # Circular toggle the order/sorting of directory views
|
|
460
|
+
case @lsorder
|
|
461
|
+
when ""
|
|
462
|
+
@lsorder = "-S"
|
|
463
|
+
w_b_info(" Sorting by size, largest first")
|
|
464
|
+
when "-S"
|
|
465
|
+
@lsorder = "-t"
|
|
466
|
+
w_b_info(" Sorting by modification time")
|
|
467
|
+
when "-t"
|
|
468
|
+
@lsorder = "-X"
|
|
469
|
+
w_b_info(" Sorting by extension (alphabetically)")
|
|
470
|
+
when "-X"
|
|
471
|
+
@lsorder = ""
|
|
472
|
+
w_b_info(" Normal sorting")
|
|
473
|
+
end
|
|
474
|
+
@w_r.update = true
|
|
475
|
+
@orderchange = true
|
|
476
|
+
when 'i' # Invert the order/sorting of directory views
|
|
477
|
+
case @lsinvert
|
|
478
|
+
when ""
|
|
479
|
+
@lsinvert = "-r"
|
|
480
|
+
w_b_info(" Sorting inverted")
|
|
481
|
+
when "-r"
|
|
482
|
+
@lsinvert = ""
|
|
483
|
+
w_b_info(" Sorting NOT inverted")
|
|
484
|
+
end
|
|
485
|
+
@w_r.update = true
|
|
486
|
+
@orderchange = true
|
|
487
|
+
when 'O' # Show the Ordering in the bottom window (the full ls command)
|
|
488
|
+
w_b_info(" Full 'ls' command: ls <@s> #{@lsbase} #{@lsall} #{@lsorder} #{@lsinvert} #{@lsuser}")
|
|
489
|
+
when 'G' # Git status for selected item or current dir
|
|
490
|
+
if File.exist?(".git")
|
|
491
|
+
w_r_info(`git status 2>/dev/null`)
|
|
492
|
+
else
|
|
493
|
+
w_r_info("This is not a git repository.")
|
|
494
|
+
end
|
|
495
|
+
@w_r.update = false
|
|
496
|
+
@w_b.update = true
|
|
497
|
+
when 'H' # Compare with previous hash status or write hash status if no existing hash
|
|
498
|
+
hashcmd = "\(find #{Dir.pwd} -type f -print0 | sort -z | xargs -0 sha1sum; find #{Dir.pwd}"\
|
|
499
|
+
" \\( -type f -o -type d \\) -print0 | sort -z | xargs -0 stat -c '%n %a'\) | sha1sum | cut -c -40"
|
|
500
|
+
begin
|
|
501
|
+
hashdir = `#{hashcmd}`.chomp
|
|
502
|
+
rescue StandardError => e
|
|
503
|
+
w_r_info("Error: #{e.inspect}")
|
|
504
|
+
end
|
|
505
|
+
hashtime = DateTime.now.strftime "%Y-%m-%d %H:%M"
|
|
506
|
+
if @hash.include?(Dir.pwd)
|
|
507
|
+
if @hash[Dir.pwd][1] == hashdir
|
|
508
|
+
w_b_info(" Hash for #{Dir.pwd} has NOT changed since #{hashtime} (#{hashdir})")
|
|
509
|
+
else
|
|
510
|
+
w_b_info(" Hash for #{Dir.pwd} has CHANGED since #{hashtime} (#{@hash[Dir.pwd][1]} -> #{hashdir})")
|
|
511
|
+
@hash[Dir.pwd] = [hashtime, hashdir]
|
|
512
|
+
end
|
|
513
|
+
else
|
|
514
|
+
hashtime = DateTime.now.strftime "%Y-%m-%d %H:%M"
|
|
515
|
+
@hash[Dir.pwd] = [hashtime, hashdir]
|
|
516
|
+
w_b_info(" New hash for #{Dir.pwd}: #{hashtime}: #{hashdir}")
|
|
517
|
+
end
|
|
518
|
+
@w_r.update = true
|
|
519
|
+
@w_b.update = false
|
|
520
|
+
# RIGHT PANE
|
|
521
|
+
when 'ENTER' # Refresh right pane
|
|
522
|
+
@w_r.clr # First clear the window, then clear any previously showing image
|
|
523
|
+
image_show("clear") if @image; @image = false
|
|
524
|
+
@w_r.update = true
|
|
525
|
+
@w_b.update = true
|
|
526
|
+
when 'TAB' # Start paging
|
|
527
|
+
if @w_r.pager == 1 and @w_r.pager_cmd != ""
|
|
528
|
+
@w_r.text = `#{@w_r.pager_cmd} 2>/dev/null`
|
|
529
|
+
end
|
|
530
|
+
if @w_r.pager_more
|
|
531
|
+
@w_r.pager += 1
|
|
532
|
+
pager_show
|
|
533
|
+
end
|
|
534
|
+
@w_b.update = true
|
|
535
|
+
when 'S-TAB' # Up one page
|
|
536
|
+
if @w_r.pager > 1
|
|
537
|
+
@w_r.pager -= 1
|
|
538
|
+
pager_show
|
|
539
|
+
end
|
|
540
|
+
@w_b.update = true
|
|
541
|
+
when 'w' # Change width of left/right panes
|
|
542
|
+
@width += 1
|
|
543
|
+
@width = 2 if @width == 7
|
|
544
|
+
@break = true
|
|
545
|
+
@w_b.update = true
|
|
546
|
+
when '-'
|
|
547
|
+
@preview = !@preview
|
|
548
|
+
@break = true
|
|
549
|
+
@w_b.update = true
|
|
550
|
+
# ADDITIONAL COMMANDS
|
|
551
|
+
when '/' # Get search string to mark items that match #
|
|
552
|
+
@w_b.nohistory = true
|
|
553
|
+
@searched = w_b_getstr("/ ", "")
|
|
554
|
+
@w_r.update = true
|
|
555
|
+
when ':' # Enter "command mode" in the bottom window - tries to execute the given command
|
|
556
|
+
@w_r.nohistory = false
|
|
557
|
+
cmd = w_b_getstr(": ", "")
|
|
558
|
+
w_b_exec(cmd)
|
|
559
|
+
when ';' # Show command history
|
|
560
|
+
w_r_info("Command history (latest on top):\n\n" + @history.join("\n"))
|
|
561
|
+
@w_b.update = true
|
|
562
|
+
when 'y', 'Y'
|
|
563
|
+
if @selected == nil
|
|
564
|
+
w_b_info(" No selected item path to copy")
|
|
565
|
+
else
|
|
566
|
+
path = Dir.pwd + "/" + @selected
|
|
567
|
+
if chr == 'Y'
|
|
568
|
+
clip = "xclip -selection clipboard"
|
|
569
|
+
w_b_info(" Path copied to clipboard")
|
|
570
|
+
else
|
|
571
|
+
clip = "xclip"
|
|
572
|
+
w_b_info(" Path copied to primary selection (paste with middle mouse button)")
|
|
573
|
+
end
|
|
574
|
+
system("echo -n '#{path}' | #{clip}")
|
|
575
|
+
end
|
|
576
|
+
when '@' # Enter "Ruby debug"
|
|
577
|
+
@w_b.nohistory = true
|
|
578
|
+
cmd = w_b_getstr("◆ ", "")
|
|
579
|
+
@w_r.clr
|
|
580
|
+
@w_r << "Command: #{cmd}\n\n"
|
|
581
|
+
@w_r.refresh
|
|
582
|
+
begin
|
|
583
|
+
eval(cmd)
|
|
584
|
+
rescue StandardError => e
|
|
585
|
+
w_r_info("Error: #{e.inspect}")
|
|
586
|
+
end
|
|
587
|
+
@w_r.update = false
|
|
588
|
+
end
|
|
589
|
+
if @w_r.update == true
|
|
590
|
+
@w_r.locate = false
|
|
591
|
+
@w_r.pager = 0
|
|
592
|
+
@w_r.pager_more = false
|
|
593
|
+
end
|
|
594
|
+
@w_r.update = true if dir != Dir.pwd
|
|
595
|
+
end
|
|
596
|
+
def conf_write
|
|
597
|
+
if File.exist?(Dir.home+'/.rtfm.conf')
|
|
598
|
+
conf = File.read(Dir.home+'/.rtfm.conf')
|
|
599
|
+
else
|
|
600
|
+
conf = ""
|
|
601
|
+
end
|
|
602
|
+
conf.sub!(/^@marks.*{.*}\n/, "")
|
|
603
|
+
conf += "@marks = #{@marks}\n"
|
|
604
|
+
conf.sub!(/^@hash.*{.*}\n/, "")
|
|
605
|
+
conf += "@hash = #{@hash}\n"
|
|
606
|
+
conf.sub!(/^@tagged.*\[.*\]\n/, "")
|
|
607
|
+
conf += "@tagged = #{@tagged}\n"
|
|
608
|
+
if @write_conf_all
|
|
609
|
+
conf.sub!(/^@lslong.*\n/, "")
|
|
610
|
+
conf += "@lslong = #{@lslong}\n"
|
|
611
|
+
conf.sub!(/^@lsall.*\n/, "")
|
|
612
|
+
conf += "@lsall = \"#{@lsall}\"\n"
|
|
613
|
+
conf.sub!(/^@width.*\n/, "")
|
|
614
|
+
conf += "@width = #{@width}\n"
|
|
615
|
+
conf.sub!(/^@border.*\n/, "")
|
|
616
|
+
conf += "@border = #{@border}\n"
|
|
617
|
+
conf.sub!(/^@preview.*\n/, "")
|
|
618
|
+
conf += "@preview = #{@preview}\n"
|
|
619
|
+
w_r_info("Press W again to write this to .rtfm.conf:\n\n" + conf)
|
|
620
|
+
if getchr == 'W'
|
|
621
|
+
w_b_info(" Parameters written to .rtfm.conf")
|
|
622
|
+
@w_r.update = true
|
|
623
|
+
else
|
|
624
|
+
w_b_info(" Config NOT updated")
|
|
625
|
+
@w_r.update = true
|
|
626
|
+
return
|
|
627
|
+
end
|
|
628
|
+
end
|
|
629
|
+
File.write(Dir.home+'/.rtfm.conf', conf)
|
|
630
|
+
end
|
|
631
|
+
# TOP WINDOW FUNCTIONS
|
|
632
|
+
def w_t_info # SHOW INFO IN @w_t
|
|
633
|
+
text = " " + ENV['USER'].to_s + "@" + `hostname 2>/dev/null`.to_s.chop + ": " + Dir.pwd + "/"
|
|
634
|
+
unless @selected == nil
|
|
635
|
+
text += @selected
|
|
636
|
+
text += " → #{File.readlink(@selected)}" if File.symlink?(@selected)
|
|
637
|
+
end
|
|
638
|
+
begin
|
|
639
|
+
text += " (#{@fspes[@index]})"
|
|
640
|
+
rescue
|
|
641
|
+
end
|
|
642
|
+
begin
|
|
643
|
+
if @selected.match(@imagefile)
|
|
644
|
+
text += `identify #{@selected_safe} | awk '{printf " [%s %s %s %s] ", $3,$2,$5,$6}' 2>/dev/null` if cmd?('identify')
|
|
645
|
+
elsif @selected.match(@pdffile)
|
|
646
|
+
info = `pdfinfo #{@selected_safe} 2>/dev/null`
|
|
647
|
+
text += " [" + info.match(/Pages:.*?(\d+)/)[1]
|
|
648
|
+
text += " " + info.match(/Page size:.*\((.*)\)/)[1] + " pages] "
|
|
649
|
+
end
|
|
650
|
+
rescue
|
|
651
|
+
end
|
|
652
|
+
if Dir.exist?(@selected.to_s)
|
|
653
|
+
begin
|
|
654
|
+
text += " [" + Dir.glob(@selected+"/*").count.to_s + " " + Dir.children(@selected).count.to_s + "]"
|
|
655
|
+
rescue
|
|
656
|
+
text += " [Denied]"
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
text = text[1..(@w_t.maxx - 3)] + "…" if text.length + 3 > @w_t.maxx
|
|
660
|
+
text += " " * (@w_t.maxx - text.length) if text.length < @w_t.maxx
|
|
661
|
+
@w_t.clr
|
|
662
|
+
@w_t.text = text
|
|
663
|
+
@w_t.write
|
|
664
|
+
end
|
|
665
|
+
# LEFT WINDOW FUNCTIONS
|
|
666
|
+
def list_dir(active) # LIST CONTENT OF A DIRECTORY (BOTH active AND RIGHT WINDOWS)
|
|
667
|
+
ix = 0; t = 0
|
|
668
|
+
if active
|
|
669
|
+
win = @w_l
|
|
670
|
+
ix = @index - @w_l.maxy/2 if @index > @w_l.maxy/2 and @files.size > @w_l.maxy - 1
|
|
671
|
+
else
|
|
672
|
+
win = @w_r
|
|
673
|
+
end
|
|
674
|
+
while ix < @files.size and t < win.maxy do
|
|
675
|
+
str = @files[ix]
|
|
676
|
+
active ? str_path = str : str_path = "#{@selected}/#{str}"
|
|
677
|
+
begin # Add items matching @tag to @tagged
|
|
678
|
+
if str.match(/#{@tag}/) and @tag != false
|
|
679
|
+
@tagged.push("\"#{Dir.pwd}/#{str}\"")
|
|
680
|
+
@tagged.uniq!
|
|
681
|
+
end
|
|
682
|
+
rescue
|
|
683
|
+
end
|
|
684
|
+
# Determine the filetype of the item
|
|
685
|
+
ftype = ""
|
|
686
|
+
ftype = str.match(/\.([^.]*$)/)[1] if str.match?(/\.([^.]*$)/)
|
|
687
|
+
# Set special filetypes (sequence matters)
|
|
688
|
+
ftype = "bd" if File.blockdev?(str_path)
|
|
689
|
+
ftype = "cd" if File.chardev?(str_path)
|
|
690
|
+
ftype = "pi" if File.pipe?(str_path)
|
|
691
|
+
ftype = "st" if File.sticky?(str_path)
|
|
692
|
+
ftype = "so" if File.socket?(str_path)
|
|
693
|
+
ftype = "ex" if File.executable?(str_path)
|
|
694
|
+
ftype = "di" if File.directory?(str_path)
|
|
695
|
+
ftype = "ln" if File.symlink?(str_path)
|
|
696
|
+
begin
|
|
697
|
+
File.stat(str_path) # Checking if not an orphaned link
|
|
698
|
+
rescue
|
|
699
|
+
ftype = "or" # Set to orphant if no link target
|
|
700
|
+
end
|
|
701
|
+
fg = 7; bold = 0; bg = 0 # Set default color
|
|
702
|
+
fg, bold = get_ls_color(ftype) unless ftype == "" # Color from LS_COLORS
|
|
703
|
+
init_pair(fg, fg, bg)
|
|
704
|
+
file_marker = color_pair(fg)
|
|
705
|
+
file_marker = file_marker | Curses::A_BOLD if bold == 1
|
|
706
|
+
if ix == @index and active
|
|
707
|
+
str = "∶" + str
|
|
708
|
+
file_marker = file_marker | Curses::A_UNDERLINE
|
|
709
|
+
wixy = win.cury
|
|
710
|
+
else
|
|
711
|
+
str = " " + str
|
|
712
|
+
end
|
|
713
|
+
file_marker = file_marker | Curses::A_REVERSE if @tagged.include?("\"#{Dir.pwd}/#{str_path}\"")
|
|
714
|
+
file_marker = file_marker | Curses::A_BLINK if str.match(/#{@searched}/) and @searched != ""
|
|
715
|
+
File.directory?(str_path) ? dir = "/" : dir = ""
|
|
716
|
+
File.symlink?(str_path) ? link = "@" : link = ""
|
|
717
|
+
str = @fspes[ix] + " " + str if @lslong
|
|
718
|
+
if str.length > win.maxx - 4
|
|
719
|
+
base_name = File.basename(str, ".*")
|
|
720
|
+
base_length = base_name.length
|
|
721
|
+
ext_name = File.extname(str)
|
|
722
|
+
ext_length = ext_name.length
|
|
723
|
+
nbl = win.maxx - 5 - ext_length # nbl: new_base_length
|
|
724
|
+
str = base_name[0..nbl] + "…" + ext_name
|
|
725
|
+
end
|
|
726
|
+
if !active and ix == win.maxy - 1 # Add indicator of more at bottom @w_r list
|
|
727
|
+
win << " ..."
|
|
728
|
+
return
|
|
729
|
+
end
|
|
730
|
+
str += link + dir
|
|
731
|
+
win.attron(file_marker) { win << str } # Implement color/bold to the item
|
|
732
|
+
win.clrtoeol
|
|
733
|
+
win << "\n"
|
|
734
|
+
ix += 1; t += 1
|
|
735
|
+
end
|
|
736
|
+
(win.maxy - win.cury).times {win.deleteln()} # Clear to bottom of window
|
|
737
|
+
if active
|
|
738
|
+
init_pair(242, 242, 0)
|
|
739
|
+
if @index > @w_l.maxy/2
|
|
740
|
+
@w_l.setpos(0, @w_l.maxx - 1)
|
|
741
|
+
@w_l.attron(color_pair(242) | Curses::A_DIM) { @w_l << "∆" }
|
|
742
|
+
end
|
|
743
|
+
if @files.length > @w_l.maxy - 1 and @files.length > @index + @w_l.maxy/2 - 1
|
|
744
|
+
@w_l.setpos(@w_l.maxy - 2, @w_l.maxx - 1)
|
|
745
|
+
@w_l.attron(color_pair(242) | Curses::A_DIM) { @w_l << "∇" }
|
|
746
|
+
end
|
|
747
|
+
end
|
|
748
|
+
end
|
|
749
|
+
def open_selected(html = nil) # OPEN SELECTED ITEM (when pressing RIGHT)
|
|
750
|
+
if File.directory?(@selected) # Rescue for permission error
|
|
751
|
+
begin
|
|
752
|
+
@marks["'"] = Dir.pwd
|
|
753
|
+
Dir.chdir(@selected)
|
|
754
|
+
rescue
|
|
755
|
+
end
|
|
756
|
+
else
|
|
757
|
+
begin
|
|
758
|
+
if File.read(@selected).force_encoding("UTF-8").valid_encoding? and not html
|
|
759
|
+
system("exec $EDITOR #{@selected_safe}")
|
|
760
|
+
else
|
|
761
|
+
if @runmailcap
|
|
762
|
+
Thread.new { system("run-mailcap #{@selected_safe} 2>/dev/null") }
|
|
763
|
+
else
|
|
764
|
+
Thread.new { system("xdg-open #{@selected_safe} 2>/dev/null") }
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
@break = true
|
|
768
|
+
rescue
|
|
769
|
+
end
|
|
770
|
+
end
|
|
771
|
+
end
|
|
772
|
+
def copy_move_link(type) # COPY OR MOVE TAGGED ITEMS (COPY IF "keep == true")
|
|
773
|
+
@tagged.uniq!
|
|
774
|
+
@tagged.each do | item |
|
|
775
|
+
item = item[1..-2]
|
|
776
|
+
dest = Dir.pwd
|
|
777
|
+
dest += "/" + File.basename(item)
|
|
778
|
+
dest += "1" if File.exist?(dest)
|
|
779
|
+
while File.exist?(dest)
|
|
780
|
+
dest = dest.chop + (dest[-1].to_i + 1).to_s
|
|
781
|
+
end
|
|
782
|
+
begin
|
|
783
|
+
case type
|
|
784
|
+
when "copy"
|
|
785
|
+
FileUtils.cp_r(item, dest)
|
|
786
|
+
when "move"
|
|
787
|
+
FileUtils.mv(item, dest)
|
|
788
|
+
when "link"
|
|
789
|
+
FileUtils.ln_s(item, dest)
|
|
790
|
+
end
|
|
791
|
+
rescue StandardError => err
|
|
792
|
+
w_b_info(err.to_s)
|
|
793
|
+
end
|
|
794
|
+
end
|
|
795
|
+
@tagged = []
|
|
796
|
+
end
|
|
797
|
+
# RIGHT WINDOW FUNCTIONS
|
|
798
|
+
def w_r_show # SHOW CONTENTS IN THE RIGHT WINDOW
|
|
799
|
+
if @w_r.update
|
|
800
|
+
@w_r.clr # First clear the window, then clear any previously showing image
|
|
801
|
+
image_show("clear") if @image; @image = false
|
|
802
|
+
end
|
|
803
|
+
begin # Determine the specific programs to open/show content
|
|
804
|
+
if @w_r.pager > 0
|
|
805
|
+
pager_show
|
|
806
|
+
elsif File.directory?(@selected)
|
|
807
|
+
ls_cmd = "ls #{@selected_safe} #{@lsbase} #{@lsall} #{@lsorder} #{@lsinvert} #{@lsuser}"
|
|
808
|
+
@files = `#{ls_cmd} 2>/dev/null`.split("\n")
|
|
809
|
+
ls_cmd += %q[ -lhgGH --time-style="long-iso" | awk '{printf "%s%12s%6s%6s%5s", $1,$4,$5,$3,$2 "\n"}']
|
|
810
|
+
@fspes = `#{ls_cmd} 2>/dev/null`.split("\n").drop(1)
|
|
811
|
+
list_dir(false)
|
|
812
|
+
# TEXT
|
|
813
|
+
elsif File.read(@selected).force_encoding("UTF-8").valid_encoding? and @w_r.pager == 0
|
|
814
|
+
begin # View the file as text if it is utf-8
|
|
815
|
+
@w_r.pager_cmd = "batcat -n --color=always #{@selected_safe} 2>/dev/null"
|
|
816
|
+
@w_r.text = `batcat -n --color=always --line-range :#{@w_r.maxy} #{@selected_safe} 2>/dev/null`
|
|
817
|
+
pager_start
|
|
818
|
+
syntax_highlight(@w_r.text)
|
|
819
|
+
rescue
|
|
820
|
+
@w_r.pager_cmd = "cat #{@selected_safe} 2>/dev/null"
|
|
821
|
+
w_r_doc
|
|
822
|
+
end
|
|
823
|
+
# PDF
|
|
824
|
+
elsif @selected.match(@pdffile) and @w_r.pager == 0
|
|
825
|
+
@w_r.pager_cmd = "pdftotext #{@selected_safe} - 2>/dev/null | less"
|
|
826
|
+
@w_r.text = `pdftotext -f 1 -l 4 #{@selected_safe} - 2>/dev/null`
|
|
827
|
+
pager_start
|
|
828
|
+
@w_r << @w_r.text
|
|
829
|
+
# OPEN/LIBREOFFICE
|
|
830
|
+
elsif @selected.match(@oolofile) and @w_r.pager == 0
|
|
831
|
+
@w_r.pager_cmd = "odt2txt #{@selected_safe} 2>/dev/null"
|
|
832
|
+
w_r_doc
|
|
833
|
+
# MS DOCX
|
|
834
|
+
elsif @selected.match(@docxfile) and @w_r.pager == 0
|
|
835
|
+
@w_r.pager_cmd = "docx2txt #{@selected_safe} - 2>/dev/null"
|
|
836
|
+
w_r_doc
|
|
837
|
+
# MS XLSX
|
|
838
|
+
elsif @selected.match(@xlsxfile) and @w_r.pager == 0
|
|
839
|
+
@w_r.pager_cmd = "ssconvert -O 'separator= ' -T Gnumeric_stf:stf_assistant #{@selected_safe} fd://1 2>/dev/null"
|
|
840
|
+
w_r_doc
|
|
841
|
+
# MS PPTX
|
|
842
|
+
elsif @selected.match(@pptxfile) and @w_r.pager == 0
|
|
843
|
+
@w_r.pager_cmd = %Q[unzip -qc #{@selected_safe} | ruby -e '$stdin.each_line { |i| i.force_encoding("ISO-8859-1").scan(/<a:t>(.+?)<\\/a:t>/).each { |j| puts(j) } }' 2>/dev/null]
|
|
844
|
+
w_r_doc
|
|
845
|
+
# MS DOC
|
|
846
|
+
elsif @selected.match(@docfile) and @w_r.pager == 0
|
|
847
|
+
@w_r.pager_cmd = "catdoc #{@selected_safe} 2>/dev/null"
|
|
848
|
+
w_r_doc
|
|
849
|
+
# MS XLS
|
|
850
|
+
elsif @selected.match(@xlsfile) and @w_r.pager == 0
|
|
851
|
+
@w_r.pager_cmd = "xls2csv #{@selected_safe} 2>/dev/null"
|
|
852
|
+
w_r_doc
|
|
853
|
+
# MS PPT
|
|
854
|
+
elsif @selected.match(@pptfile) and @w_r.pager == 0
|
|
855
|
+
@w_r.pager_cmd = "catppt #{@selected_safe} 2>/dev/null"
|
|
856
|
+
w_r_doc
|
|
857
|
+
# IMAGES
|
|
858
|
+
elsif @selected.match(@imagefile)
|
|
859
|
+
image_show(@selected_safe)
|
|
860
|
+
@image = true
|
|
861
|
+
# VIDEOS (THUMBNAILS)
|
|
862
|
+
elsif @selected.match(/\.mpg$|\.mpeg$|\.avi$|\.mov$|\.mkv$|\.mp4$/)
|
|
863
|
+
begin
|
|
864
|
+
tmpfile = "/tmp/" + File.basename(@selected_safe,".*")
|
|
865
|
+
`ffmpegthumbnailer -s 1200 -i #{@selected_safe} -o /tmp/rtfm_video_tn.jpg 2>/dev/null`
|
|
866
|
+
image_show("/tmp/rtfm_video_tn.jpg")
|
|
867
|
+
@image = true
|
|
868
|
+
rescue
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
rescue
|
|
872
|
+
end
|
|
873
|
+
pager_add_markers # Add page markers, up and/or down
|
|
874
|
+
@w_r.update = false
|
|
875
|
+
@w_r.refresh
|
|
876
|
+
end
|
|
877
|
+
def w_r_doc # GET FULL CONTENT TO PAGE
|
|
878
|
+
@w_r.text = `#{@w_r.pager_cmd} 2>/dev/null`
|
|
879
|
+
pager_start
|
|
880
|
+
@w_r << @w_r.text
|
|
881
|
+
end
|
|
882
|
+
def w_r_info(info) # SHOW INFO IN THE RIGHT WINDOW
|
|
883
|
+
@w_r.text = info
|
|
884
|
+
@w_r.pager_cmd = ""
|
|
885
|
+
pager_start
|
|
886
|
+
pager_show
|
|
887
|
+
@w_r.update = false
|
|
888
|
+
image_show("clear") if @image; @image = false
|
|
889
|
+
end
|
|
890
|
+
def marks_info # SHOW MARKS IN RIGHT WINDOW
|
|
891
|
+
info = "Marks:\n"
|
|
892
|
+
unless @marks.empty?
|
|
893
|
+
@marks.each do |mark, dir|
|
|
894
|
+
info += "#{mark} = #{dir}\n"
|
|
895
|
+
end
|
|
896
|
+
else
|
|
897
|
+
info += "(none)"
|
|
898
|
+
end
|
|
899
|
+
w_r_info(info)
|
|
900
|
+
end
|
|
901
|
+
def tagged_info # SHOW THE LIST OF TAGGED ITEMS IN @w_r
|
|
902
|
+
info = "Tagged:\n"
|
|
903
|
+
@tagged.empty? ? info += "(None)" : info += @tagged.join("\n")
|
|
904
|
+
w_r_info(info)
|
|
905
|
+
end
|
|
906
|
+
def syntax_highlight(input) # BATCAT SYNTAX HIGHLIGHTING
|
|
907
|
+
color_ary = color_parse(input)
|
|
908
|
+
color_ary.each do | pair |
|
|
909
|
+
begin
|
|
910
|
+
fg = pair[0].to_i
|
|
911
|
+
text = pair[1]
|
|
912
|
+
text.gsub!(/\t/, '')
|
|
913
|
+
init_pair(fg, fg, 0)
|
|
914
|
+
@w_r.attron(color_pair(fg)) { @w_r << text }
|
|
915
|
+
rescue
|
|
916
|
+
end
|
|
917
|
+
end
|
|
918
|
+
end
|
|
919
|
+
def image_show(image)# SHOW THE SELECTED IMAGE IN THE RIGHT WINDOW
|
|
920
|
+
# Pass "clear" to clear the window for previous image
|
|
921
|
+
return unless @showimage
|
|
922
|
+
begin
|
|
923
|
+
terminfo = `xwininfo -id $(xdotool getactivewindow 2>/dev/null) 2>/dev/null`
|
|
924
|
+
term_w = terminfo.match(/Width: (\d+)/)[1].to_i
|
|
925
|
+
term_h = terminfo.match(/Height: (\d+)/)[1].to_i
|
|
926
|
+
char_w = term_w / Curses.cols
|
|
927
|
+
char_h = term_h / Curses.lines
|
|
928
|
+
img_x = char_w * (Curses.cols/@width + 1)
|
|
929
|
+
img_y = char_h * 2
|
|
930
|
+
img_max_w = char_w * (Curses.cols - Curses.cols/@width - 2)
|
|
931
|
+
img_max_h = char_h * (Curses.lines - 4)
|
|
932
|
+
if image == "clear"
|
|
933
|
+
img_x -= char_w
|
|
934
|
+
img_max_w += char_w + 2
|
|
935
|
+
img_max_h += 2
|
|
936
|
+
`echo "6;#{img_x};#{img_y};#{img_max_w};#{img_max_h};\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
|
|
937
|
+
else
|
|
938
|
+
img_w,img_h = `identify -format "%[fx:w]x%[fx:h]" #{image} 2>/dev/null`.split('x')
|
|
939
|
+
img_w = img_w.to_i
|
|
940
|
+
img_h = img_h.to_i
|
|
941
|
+
if img_w > img_max_w
|
|
942
|
+
img_h = img_h * img_max_w / img_w
|
|
943
|
+
img_w = img_max_w
|
|
944
|
+
end
|
|
945
|
+
if img_h > img_max_h
|
|
946
|
+
img_w = img_w * img_max_h / img_h
|
|
947
|
+
img_h = img_max_h
|
|
948
|
+
end
|
|
949
|
+
`echo "0;1;#{img_x};#{img_y};#{img_w};#{img_h};;;;;\"#{image}\"\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
|
|
950
|
+
end
|
|
951
|
+
rescue
|
|
952
|
+
@w_r.clr
|
|
953
|
+
@w_r << "Error showing image"
|
|
954
|
+
end
|
|
955
|
+
end
|
|
956
|
+
def pager_start # START PAGING
|
|
957
|
+
@w_r.pager = 1
|
|
958
|
+
if @w_r.text.lines.count > @w_r.maxy - 2
|
|
959
|
+
@w_r.pager_more = true
|
|
960
|
+
end
|
|
961
|
+
end
|
|
962
|
+
def pager_show # SHOW THE CURRENT PAGE CONTENT
|
|
963
|
+
@w_r.setpos(0,0)
|
|
964
|
+
beg_l = (@w_r.pager - 1) * (@w_r.maxy - 5)
|
|
965
|
+
end_l = beg_l + @w_r.maxy - 2
|
|
966
|
+
input = @w_r.text.lines[beg_l..end_l].join() + "\n"
|
|
967
|
+
input.lines.count > @w_r.maxy - 2 ? @w_r.pager_more = true : @w_r.pager_more = false
|
|
968
|
+
if @w_r.pager_cmd.match(/batcat/)
|
|
969
|
+
syntax_highlight(input)
|
|
970
|
+
else
|
|
971
|
+
@w_r << input
|
|
972
|
+
end
|
|
973
|
+
(@w_r.maxy - @w_r.cury).times {@w_r.deleteln()} # Clear to bottom of window
|
|
974
|
+
pager_add_markers
|
|
975
|
+
@w_r.refresh
|
|
976
|
+
end
|
|
977
|
+
def pager_add_markers # ADD MARKERS TOP/RIGHT & BOTTOM/RIGHT TO SHOW PAGING AS RELEVANT
|
|
978
|
+
if @w_r.pager > 1
|
|
979
|
+
@w_r.setpos(0, @w_r.maxx - 2)
|
|
980
|
+
@w_r << " ∆"
|
|
981
|
+
end
|
|
982
|
+
if @w_r.pager_more
|
|
983
|
+
@w_r.setpos(@w_r.maxy - 1, @w_r.maxx - 2)
|
|
984
|
+
@w_r << " ∇"
|
|
985
|
+
end
|
|
986
|
+
end
|
|
987
|
+
def var_resets # RESET PAGER VARIABLES
|
|
988
|
+
@pager = 0
|
|
989
|
+
@pager_more = false
|
|
990
|
+
@pager_cmd = ""
|
|
991
|
+
@info = false
|
|
992
|
+
end
|
|
993
|
+
# BOTTOM WINDOW FUNCTIONS
|
|
994
|
+
def w_b_info(info) # SHOW INFO IN @W_B
|
|
995
|
+
@w_b.clr
|
|
996
|
+
info = ": for command (use @s for selected item, @t for tagged items)" if info == nil
|
|
997
|
+
info = info[1..(@w_b.maxx - 3)] + "…" if info.length + 3 > @w_b.maxx
|
|
998
|
+
info += " " * (@w_b.maxx - info.length) if info.length < @w_b.maxx
|
|
999
|
+
@w_b.text = info
|
|
1000
|
+
@w_b.write
|
|
1001
|
+
@w_b.update = false
|
|
1002
|
+
end
|
|
1003
|
+
def w_b_getstr(pretext, text) # A SIMPLE READLINE-LIKE ROUTINE
|
|
1004
|
+
Curses.curs_set(1)
|
|
1005
|
+
Curses.echo
|
|
1006
|
+
stk = 0
|
|
1007
|
+
@history.insert(stk, text)
|
|
1008
|
+
pos = @history[stk].length
|
|
1009
|
+
chr = ""
|
|
1010
|
+
while chr != "ENTER"
|
|
1011
|
+
@w_b.setpos(0,0)
|
|
1012
|
+
init_pair(250, 250, 238)
|
|
1013
|
+
text = pretext + @history[stk]
|
|
1014
|
+
text += " " * (@w_b.maxx - text.length) if text.length < @w_b.maxx
|
|
1015
|
+
@w_b.attron(color_pair(250)) { @w_b << text }
|
|
1016
|
+
@w_b.setpos(0,pretext.length + pos)
|
|
1017
|
+
@w_b.refresh
|
|
1018
|
+
chr = getchr
|
|
1019
|
+
case chr
|
|
1020
|
+
when 'UP'
|
|
1021
|
+
unless @w_b.nohistory
|
|
1022
|
+
unless stk == @history.length - 1
|
|
1023
|
+
stk += 1
|
|
1024
|
+
pos = @history[stk].length
|
|
1025
|
+
end
|
|
1026
|
+
end
|
|
1027
|
+
when 'DOWN'
|
|
1028
|
+
unless @w_b.nohistory
|
|
1029
|
+
unless stk == 0
|
|
1030
|
+
stk -= 1
|
|
1031
|
+
pos = @history[stk].length
|
|
1032
|
+
end
|
|
1033
|
+
end
|
|
1034
|
+
when 'RIGHT'
|
|
1035
|
+
pos += 1 unless pos > @history[stk].length
|
|
1036
|
+
when 'LEFT'
|
|
1037
|
+
pos -= 1 unless pos == 0
|
|
1038
|
+
when 'HOME'
|
|
1039
|
+
pos = 0
|
|
1040
|
+
when 'END'
|
|
1041
|
+
pos = @history[stk].length
|
|
1042
|
+
when 'DEL'
|
|
1043
|
+
@history[stk][pos] = ""
|
|
1044
|
+
when 'BACK'
|
|
1045
|
+
unless pos == 0
|
|
1046
|
+
pos -= 1
|
|
1047
|
+
@history[stk][pos] = ""
|
|
1048
|
+
end
|
|
1049
|
+
when 'WBACK'
|
|
1050
|
+
unless pos == 0
|
|
1051
|
+
until @history[stk][pos - 1] == " " or pos == 0
|
|
1052
|
+
pos -= 1
|
|
1053
|
+
@history[stk][pos] = ""
|
|
1054
|
+
end
|
|
1055
|
+
if @history[stk][pos - 1] == " "
|
|
1056
|
+
pos -= 1
|
|
1057
|
+
@history[stk][pos] = ""
|
|
1058
|
+
end
|
|
1059
|
+
end
|
|
1060
|
+
when 'LDEL'
|
|
1061
|
+
@history[stk] = ""
|
|
1062
|
+
pos = 0
|
|
1063
|
+
when 'TAB' # Tab completion of dirs and files
|
|
1064
|
+
p1 = pos - 1
|
|
1065
|
+
c = @history[stk][0..(p1)].sub(/^.* /, '')
|
|
1066
|
+
p0 = p1 - c.length
|
|
1067
|
+
compl = File.expand_path(c)
|
|
1068
|
+
compl += "/" if Dir.exist?(compl)
|
|
1069
|
+
clist = Dir.glob(compl + "*")
|
|
1070
|
+
unless compl == clist[0].to_s and clist.length == 1
|
|
1071
|
+
if clist.length == 1
|
|
1072
|
+
compl = clist[0].to_s
|
|
1073
|
+
else
|
|
1074
|
+
ix = clist.find_index(compl)
|
|
1075
|
+
ix = 0 if ix == nil
|
|
1076
|
+
sel_item = ""
|
|
1077
|
+
begin
|
|
1078
|
+
Curses.curs_set(0)
|
|
1079
|
+
Curses.noecho
|
|
1080
|
+
@w_r.clr
|
|
1081
|
+
@w_r << "Completion list:\n\n"
|
|
1082
|
+
clist.each.with_index do |item, index|
|
|
1083
|
+
if index == ix
|
|
1084
|
+
@w_r.attron(Curses::A_BLINK) { @w_r << item }
|
|
1085
|
+
sel_item = item
|
|
1086
|
+
else
|
|
1087
|
+
@w_r << item
|
|
1088
|
+
end
|
|
1089
|
+
@w_r << "\n"
|
|
1090
|
+
end
|
|
1091
|
+
@w_r.refresh
|
|
1092
|
+
ix == clist.length ? ix = 0 : ix += 1
|
|
1093
|
+
end while getchr == 'TAB'
|
|
1094
|
+
compl = sel_item
|
|
1095
|
+
@w_r.clr
|
|
1096
|
+
Curses.curs_set(1)
|
|
1097
|
+
Curses.echo
|
|
1098
|
+
end
|
|
1099
|
+
end
|
|
1100
|
+
@history[stk].sub!(c,compl)
|
|
1101
|
+
pos = pos - c.length + compl.length
|
|
1102
|
+
when /^.$/
|
|
1103
|
+
@history[stk].insert(pos,chr)
|
|
1104
|
+
pos += 1
|
|
1105
|
+
end
|
|
1106
|
+
end
|
|
1107
|
+
curstr = @history[stk]
|
|
1108
|
+
@history.shift if @w_b.nohistory
|
|
1109
|
+
unless @w_b.nohistory
|
|
1110
|
+
@history.uniq!
|
|
1111
|
+
@history.compact!
|
|
1112
|
+
@history.delete("")
|
|
1113
|
+
end
|
|
1114
|
+
Curses.curs_set(0)
|
|
1115
|
+
Curses.noecho
|
|
1116
|
+
return curstr
|
|
1117
|
+
end
|
|
1118
|
+
def w_b_exec(cmd) # EXECUTE COMMAND FROM @W_B
|
|
1119
|
+
# Subsitute any '@s' with the selected item, @t with tagged items
|
|
1120
|
+
# 'rm @s' deletes the selected item, 'rm @t' deletes tagged items
|
|
1121
|
+
return if cmd == ""
|
|
1122
|
+
@s = "\"#{Dir.pwd}/#{@selected}\""
|
|
1123
|
+
cmd.gsub!(/@s/, @s)
|
|
1124
|
+
@t = @tagged.join(" ")
|
|
1125
|
+
cmd.gsub!(/@t/, @t)
|
|
1126
|
+
if cmd.match(/^cd /)
|
|
1127
|
+
cmd.sub!(/^cd (\S*).*/, '\1')
|
|
1128
|
+
Dir.chdir(cmd) if Dir.exist?(cmd)
|
|
1129
|
+
return
|
|
1130
|
+
end
|
|
1131
|
+
begin
|
|
1132
|
+
begin
|
|
1133
|
+
@w_r.pager_cmd = "#{cmd} | batcat -n --color=always 2>/dev/null"
|
|
1134
|
+
@w_r.text = `#{@w_r.pager_cmd} 2>/dev/null`
|
|
1135
|
+
rescue
|
|
1136
|
+
@w_r.text = `#{cmd} 2>/dev/null`
|
|
1137
|
+
end
|
|
1138
|
+
unless @w_r.text == "" or @w_r.text == nil
|
|
1139
|
+
pager_start
|
|
1140
|
+
pager_show
|
|
1141
|
+
@w_r.update = false
|
|
1142
|
+
end
|
|
1143
|
+
rescue
|
|
1144
|
+
w_b_info(" Failed to execute command (#{cmd})")
|
|
1145
|
+
end
|
|
1146
|
+
end
|
|
1147
|
+
|
|
1148
|
+
# MAIN PROGRAM
|
|
1149
|
+
loop do # OUTER LOOP - CATCHING REFRESHES VIA 'r'
|
|
1150
|
+
@break = false # Initialize @break variable (set if user hits 'r')
|
|
1151
|
+
@image = false # Set the image flag to false (set if image is displayed in @w_r)
|
|
1152
|
+
@tag = false # Set pattern tagging to nothing
|
|
1153
|
+
@orderchange = false
|
|
1154
|
+
begin # Create the four windows/panels
|
|
1155
|
+
if @border
|
|
1156
|
+
Curses.stdscr.bg = 236 # Use for borders
|
|
1157
|
+
Curses.stdscr.fill
|
|
1158
|
+
else
|
|
1159
|
+
Curses.stdscr.clear
|
|
1160
|
+
Curses.stdscr.refresh
|
|
1161
|
+
end
|
|
1162
|
+
maxx = Curses.cols
|
|
1163
|
+
maxy = Curses.lines
|
|
1164
|
+
# Curses::Window.new(h,w,y,x)
|
|
1165
|
+
@w_t = Curses::Window.new(1, 0, 0, 0)
|
|
1166
|
+
@w_b = Curses::Window.new(1, 0, maxy - 1, 0)
|
|
1167
|
+
@w_l = Curses::Window.new(maxy - 3, (maxx/@width) - 1, 2, 0)
|
|
1168
|
+
@w_r = Curses::Window.new(maxy - 4, maxx - (maxx/@width), 2, maxx/@width)
|
|
1169
|
+
@w_t.fg, @w_t.bg = 232, 249
|
|
1170
|
+
@w_t.attr = Curses::A_BOLD
|
|
1171
|
+
@w_b.fg, @w_b.bg = 250, 238
|
|
1172
|
+
@w_t.update = true
|
|
1173
|
+
@w_b.update = true
|
|
1174
|
+
@w_l.update = true
|
|
1175
|
+
@w_r.update = true
|
|
1176
|
+
@w_r.pager = 0
|
|
1177
|
+
@w_r.pager_more = false
|
|
1178
|
+
dir_old = Dir.pwd
|
|
1179
|
+
lsall_old = @lsall
|
|
1180
|
+
unless @tagged.empty?
|
|
1181
|
+
tagged_info
|
|
1182
|
+
@w_r.update = false
|
|
1183
|
+
end
|
|
1184
|
+
loop do # INNER, CORE LOOP
|
|
1185
|
+
system("printf \"\033]0;RTFM: #{Dir.pwd}\007\"") # Set Window title to path
|
|
1186
|
+
ls_cmd = "ls #{@lsbase} #{@lsall} #{@lsorder} #{@lsinvert} #{@lsuser}" # Get files in current directory
|
|
1187
|
+
@files = `#{ls_cmd} 2>/dev/null`.split("\n")
|
|
1188
|
+
ls_cmd += %q[ -lhgG --time-style="long-iso" | awk '{printf "%s%12s%6s%6s%5s", $1,$4,$5,$3,$2 "\n"}']
|
|
1189
|
+
@fspes = `#{ls_cmd} 2>/dev/null`.split("\n").drop(1)
|
|
1190
|
+
if Dir.pwd != dir_old
|
|
1191
|
+
if @directory.key?(Dir.pwd)
|
|
1192
|
+
@selected = @directory[Dir.pwd]
|
|
1193
|
+
@index = @files.index(@selected)
|
|
1194
|
+
else
|
|
1195
|
+
@index = 0
|
|
1196
|
+
end
|
|
1197
|
+
end
|
|
1198
|
+
dir_old = Dir.pwd
|
|
1199
|
+
@index = 0 if @index == nil
|
|
1200
|
+
index_old = @index
|
|
1201
|
+
if @orderchange # Change in ordering must be handled
|
|
1202
|
+
@index = @files.index(@selected)
|
|
1203
|
+
@orderchange = false
|
|
1204
|
+
end
|
|
1205
|
+
@index = @files.index(@selected) if @lsall != lsall_old # Change in showing all items must be handled
|
|
1206
|
+
@index = index_old if @files.index(@selected) == nil # If item no longer is shown
|
|
1207
|
+
@min_index = 0
|
|
1208
|
+
@max_index = @files.size - 1
|
|
1209
|
+
@index = @max_index if @index > @max_index # If deleted many items
|
|
1210
|
+
@index = 0 if @index < 0
|
|
1211
|
+
@selected = @files[@index] # Get text of selected item
|
|
1212
|
+
@selected_safe = "\"#{@selected}\"" # Make it safe for commands
|
|
1213
|
+
# Top window (info line)
|
|
1214
|
+
w_t_info
|
|
1215
|
+
# Bottom window (command line) Before @w_r to avoid image dropping out on startup
|
|
1216
|
+
w_b_info(nil) if @w_b.update
|
|
1217
|
+
# Left and right windows (browser & content viewer)
|
|
1218
|
+
@w_l.setpos(0,0)
|
|
1219
|
+
list_dir(true)
|
|
1220
|
+
@w_l.refresh
|
|
1221
|
+
w_r_show if @w_r.update and @preview
|
|
1222
|
+
Curses.curs_set(1) # Clear residual cursor
|
|
1223
|
+
Curses.curs_set(0) # ...from editing files
|
|
1224
|
+
@tag = false # Clear tag pattern
|
|
1225
|
+
lsall_old = @lsall
|
|
1226
|
+
main_getkey # Get key from user
|
|
1227
|
+
break if @break # Break to outer loop, redrawing windows, if user hit 'r'
|
|
1228
|
+
break if Curses.cols != maxx or Curses.lines != maxy # break on terminal resize
|
|
1229
|
+
end
|
|
1230
|
+
ensure # On exit: close curses, clear terminal
|
|
1231
|
+
@write_conf_all = false
|
|
1232
|
+
conf_write if @write_conf # Write marks to config file
|
|
1233
|
+
image_show("clear")
|
|
1234
|
+
close_screen
|
|
1235
|
+
# If launched via the script "r", return current dir and "r" will cd to that
|
|
1236
|
+
File.write(ARGV[0], Dir.pwd) if ARGV[0] and ARGV[0].match(/\/tmp\/tmp/)
|
|
1237
|
+
end
|
|
1238
|
+
end
|
|
1239
|
+
|
|
1240
|
+
# vim: set sw=2 sts=2 et fdm=syntax fdn=2 fcs=fold\:\ :
|
metadata
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rtfm-filemanager
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Geir Isene
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-09-23 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: RTFM lets you browse directories and view the content of directories
|
|
14
|
+
and files. Files are syntax highlighted, images are shown in the terminal, videos
|
|
15
|
+
are thumbnailed, etc. You can bookmark and jump around easily, delete, rename, copy,
|
|
16
|
+
symlink and move files. RTFM has a a wide range of other features.
|
|
17
|
+
email: g@isene.com
|
|
18
|
+
executables:
|
|
19
|
+
- rtfm
|
|
20
|
+
extensions: []
|
|
21
|
+
extra_rdoc_files: []
|
|
22
|
+
files:
|
|
23
|
+
- ".rtfm.launch"
|
|
24
|
+
- bin/rtfm
|
|
25
|
+
homepage: https://isene.com/
|
|
26
|
+
licenses:
|
|
27
|
+
- Unlicense
|
|
28
|
+
metadata:
|
|
29
|
+
source_code_uri: https://github.com/isene/RTFM
|
|
30
|
+
post_install_message:
|
|
31
|
+
rdoc_options: []
|
|
32
|
+
require_paths:
|
|
33
|
+
- lib
|
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0'
|
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
requirements: []
|
|
45
|
+
rubygems_version: 3.1.2
|
|
46
|
+
signing_key:
|
|
47
|
+
specification_version: 4
|
|
48
|
+
summary: RTFM - Ruby Terminal File Manager
|
|
49
|
+
test_files: []
|