From adfea091f816cc2f4007e99b6b2be35a821857da Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 8 Apr 2009 00:00:00 +0200 Subject: the first usable version. I was not using git yet, this was a simple backup --- ChangeLog | 9 ++ code/extensions.rb | 176 +++++++++++++++++++++++++ code/fm.rb | 357 +++++++++++++++++++++++++++++++++++++++++++++++++++ code/keys.rb | 323 ++++++++++++++++++++++++++++++++++++++++++++++ code/old_fm.rb | 233 +++++++++++++++++++++++++++++++++ code/types.rb | 26 ++++ fm | 41 ++++++ interface/ncurses.rb | 182 ++++++++++++++++++++++++++ 8 files changed, 1347 insertions(+) create mode 100644 ChangeLog create mode 100644 code/extensions.rb create mode 100644 code/fm.rb create mode 100644 code/keys.rb create mode 100644 code/old_fm.rb create mode 100644 code/types.rb create mode 100755 fm create mode 100644 interface/ncurses.rb diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..bb331506 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,9 @@ +Version 0.1 + +2009-04-08 hut + Well-running Version with some bugs here and there. + Starting new minor version + +2009-04-03 hut + Project was created. + diff --git a/code/extensions.rb b/code/extensions.rb new file mode 100644 index 00000000..db238614 --- /dev/null +++ b/code/extensions.rb @@ -0,0 +1,176 @@ +class Directory + def initialize(path) + @path = path + @pos = 0 + @pointed_file = '' + @width = 0 + refresh + end + + attr_reader(:path, :files, :pos, :width, :infos) + + def pos=(x) + @pos = x + @pointed_file = @files[x] + resize + end + + def pointed_file=(x) + if @files.include?(x) + @pointed_file = x + @pos = @files.index(x) + else + self.pos = 0 + end + resize + end + + def size() @files.size end + + def resize() + pos = Fm.get_offset(self, lines) + if @files.empty? + @width = 0 + else + @width = 0 + @files[pos, lines-2].each_with_index do |fn, ix| + ix += pos +# log File.basename(fn) + @infos[ix] + sz = File.basename(fn).size + @infos[ix].size + 2 + @width = sz if @width < sz + end +# @width = @files[pos,lines-2].map{|x| File.basename(x).size}.max + end + end + + def get_file_infos() + @infos = [] + @files.each do |fn| + if File.directory?(fn) + begin + sz = Dir.entries(fn).size - 2 + rescue + sz = "?" + end + @infos << "#{sz}" + else + if File.size?(fn) + @infos << " #{File.size(fn).bytes 2}" + else + @infos << "" + end + end + end + end + + def refresh() + glob = Dir.new(@path).to_a.sort! + if OPTIONS['hidden'] + glob -= ['.', '..', 'lost+found'] + else + glob.reject!{|x| x[0] == ?. or x == 'lost+found'} + end + if glob.empty? + glob = ['.'] + end + glob.map!{|x| File.join(@path, x)} + dirs = glob.select{|x| File.directory?(x)} + @files = dirs + (glob - dirs) + + get_file_infos + resize + + if @pos >= @files.size + @pos = @files.size - 1 + elsif @files.include?(@pointed_file) + @pos = @files.index(@pointed_file) + end + end +end + + +class File + MODES_HASH = { + '0' => '---', + '1' => '--x', + '2' => '-w-', + '3' => '-wx', + '4' => 'r--', + '5' => 'r-x', + '6' => 'rw-', + '7' => 'rwx' + } + def self.modestr(f) + unless exists?(f) + return '----------' + end + + if symlink?(f) + result = 'l' + elsif directory?(f) + result = 'd' + else + result = '-' + end + + s = ("%o" % File.stat(f).mode)[-3..-1] + for m in s.each_char + result << MODES_HASH[m] + end + + result + end +end + +class Numeric + def limit(max, min = 0) + self < min ? min : (self > max ? max : self) + end + + def bytes n_round = 2 + n = 1024 + a = %w(B K M G T Y) + + i = 0 + flt = self.to_f + + while flt > n and i < a.length - 1 + flt /= n + i += 1 + end + +# flt = flt.round(n_round) + r = 10 ** n_round + flt *= r + flt = flt.round.to_f / r + int = flt.to_i + flt = int if int == flt + + return flt.to_s + ' ' + a[i] + end +end + +class Array + def wrap(n) + n.times { push shift } + end +end + +class String + def clear + replace String.new + end + def sh + res = dup + res.gsub!('\\\\', "\000") + res.gsub!(' ', '\\ ') + res.gsub!('(', '\\(') + res.gsub!(')', '\\)') + res.gsub!('*', '\\*') + res.gsub!('\'', '\\\'') + res.gsub!('"', '\\"') + res.gsub!("\000", '\\\\') + return res + end +end + diff --git a/code/fm.rb b/code/fm.rb new file mode 100644 index 00000000..307866f8 --- /dev/null +++ b/code/fm.rb @@ -0,0 +1,357 @@ + +OPTIONS = { + 'hidden' => false, +} + +module Fm + def self.initialize + @buffer = '' + @pwd = '' + @copy = [] + @ignore_until = nil + @trash = File.expand_path('~/.trash') + pwd = Dir.getwd + + @memory = { + '`' => pwd, + '\'' => pwd + } + + @fmrc = File.expand_path('~/.fmrc') + if (File.exists?(@fmrc)) + loaded = Marshal.load(File.read(@fmrc)) + if Hash === loaded + @memory.update(loaded) + end + end + + @memory['0'] = pwd + + @dirs = Hash.new() do |hash, key| + hash[key] = Directory.new(key) + end + + @path = [@dirs['/']] + enter_dir(Dir.pwd) + end + attr_reader(:dirs, :pwd) + + VI = "vi -c 'map h :quit' -c 'map q :quit' -c 'map H :unmap h:unmap H' %s" + + def self.dump + remember_dir + dumped = Marshal.dump(@memory) + File.open(@fmrc, 'w') do |f| + f.write(dumped) + end + end + + def self.rescue_me + @buffer = '' + sleep 0.2 + end + + def self.main_loop + while true + begin + draw() + rescue Interrupt + rescue_me + end + begin + press(geti) + rescue Interrupt + rescue_me + end + end + end + + def self.current_path() @pwd.path end + + def self.enter_dir(dir) + dir = File.expand_path(dir) + + oldpath = @path.dup + + # NOTE: @dirs[unknown] is not nil but Directory.new(unknown) + @path = [@dirs['/']] + unless dir == '/' + dir.slice(0) + accumulated = '/' + for part in dir.split('/') + unless part.empty? + accumulated = File.join(accumulated, part) + @path << @dirs[accumulated] + end + end + end + @pwd = @path.last + set_title "fm: #{@pwd.path}" + + if @path.size < oldpath.size + @pwd.pos = @pwd.files.index(oldpath.last.path) || 0 + end + + i = 0 + + @path.each_with_index do |p, i| + p.refresh + unless i == @path.size - 1 + p.pointed_file = @path[i+1].path + end + end + + Dir.chdir(@pwd.path) + end + + def self.currentfile + @dirs[@currentdir][1][@dirs[@currentdir][0] || 0] + end + def self.currentfile() @pwd.files.at(@pwd.pos) end + + def self.get_offset(dir, max) + pos = dir.pos + len = dir.files.size + max -= 2 + if len <= max or pos < max/2 + return 0 + elsif pos >= (len - max/2) + return len - max + else + return pos - max/2 + end + end + + COLUMNS = 4 + + def self.get_boundaries(column) + cols = Interface.cols # to cache + case column + when 0 + return 0, cols / 8 - 1 + + when 1 + q = cols / 8 + return q, q + + when 2 + q = cols / 4 + w = @path.last.width.limit(cols/2, cols/8) + 1 + return q, w + + when 3 + l = cols / 4 + 1 + l += @path.last.width.limit(cols/2, cols/8) + + return l, cols - l + + end + end + + def self.put_directory(c, d) + l = 0 + if d + infos = (c == COLUMNS - 2) + left, wid = get_boundaries(c) + + offset = get_offset(d, lines) + (lines - 1).times do |l| + lpo = l + offset + bg = -1 + break if (f = d.files[lpo]) == nil + + dir = false + if File.symlink?(f) + bld = true + if File.exists?(f) + clr = [6, bg] + else + clr = [1, bg] + end + dir = File.directory?(f) + elsif File.directory?(f) + bld = true + dir = true + clr = [4, bg] + elsif File.executable?(f) + bld = true + clr = [2, bg] + else + bld = false + clr = [7, bg] + end + + fn = File.basename(f) + if infos + myinfo = " #{d.infos[lpo]} " + str = fn[0, wid-1].ljust(wid) + if str.size > myinfo.size + str[-myinfo.size..-1] = myinfo + yes = true + else + yes = false + end + puti l+1, left, str + if dir and yes + args = l+1, left+wid-myinfo.size, myinfo.size, *clr + color_bold_at(*args) + end + else + puti l+1, left, fn[0, wid-1].ljust(wid+1) + end + + args = l+1, left, fn.size.limit(wid-1), *clr + + if d.pos == lpo + color_reverse_at(*args) + else + if bld then color_bold_at(*args) else color_at(*args) end + end + end + end + + column_clear(c, l) + end + + def self.column_clear(n, from=0) + color(-1,-1) + left, wid = get_boundaries(n) + (from -1).upto(lines) do |l| + puti l+2, left, ' ' * (wid) + end + end + + def self.column_put_file(n, file) + m = lines - 2 + i = 0 + color 7 + bold false + File.open(file, 'r') do |f| + check = true + left, wid = get_boundaries(n) + f.lines.each do |l| + if check + check = false + break unless l.each_char.all? {|x| x[0] > 0 and x[0] < 128} + end + puti i+1, left, l.gsub("\t"," ")[0, wid-1].ljust(wid) + i += 1 + break if i == m + end + end + column_clear(n, i) + end + + def self.draw + bold false + @cur_y = get_boundaries(COLUMNS-2)[0] + @pwd.get_file_infos + + s1 = " " + s2 = "#{@path.last.path}#{"/" unless @path.size == 1}" + f = currentfile + s3 = "#{f ? File.basename(f) : ''}" + + puti 0, (s1 + s2 + s3).ljust(cols) + + bg = -1 + color_at 0, 0, -1, 7, bg + color_at 0, 0, s1.size, 7, bg + color_at 0, s1.size, s2.size, 6, bg + color_at 0, s1.size + s2.size, s3.size, 5, bg + + bold false + + f = currentfile + begin + if File.directory?(f) + put_directory(3, @dirs[currentfile]) + else + column_put_file(3, currentfile) + end + rescue + column_clear(3) + end + + pos_constant = @path.size - COLUMNS + 1 + + (COLUMNS - 1).times do |c| + pos = pos_constant + c + + if pos >= 0 + put_directory(c, @path[pos]) + else + column_clear(c) + end + end + + bold false + color -1, -1 + puti -1, "#@buffer #{@pwd.pos+1},#{@pwd.size},#{@path.size} ".rjust(cols) + more = '' + if File.symlink?(currentfile) + more = "#{File.readlink(currentfile)}" + end + puti -1, " #{Time.now.strftime("%H:%M:%S %a %b %d")} #{File.modestr(currentfile)} #{more}" + + color_at -1, 23, 10, (File.writable?(currentfile) ? 6 : 5), -1 + if more + color_at -1, 34, more.size, (File.exists?(currentfile) ? 6 : 1), -1 + end + + movi(@pwd.pos + 1 - get_offset(@pwd, lines), @cur_y) + end + + def self.enter_dir_safely(dir) + dir = File.expand_path(dir) + if File.exists?(dir) and File.directory?(dir) + olddir = @pwd.path + begin + enter_dir(dir) + return true + rescue + enter_dir(olddir) + return false + end + end + end + + def self.move_to_trash!(fn) + unless File.exists?(@trash) + Dir.mkdir(@trash) + end + new_path = File.join(@trash, File.basename(fn)) + + closei + system('mv','-v', fn, new_path) + starti + + return new_path + end + + def self.in_trash?(fn) + fn[0,@trash.size] == @trash + end + + def self.move_to_trash(fn) + if fn and File.exists?(fn) + if File.directory?(fn) + if !in_trash?(fn) and Dir.entries(fn).size > 2 + return move_to_trash!(fn) + else + Dir.rmdir(fn) rescue nil + end + elsif File.symlink?(fn) + File.delete(fn) + else + if !in_trash?(fn) and File.size?(fn) + return move_to_trash!(fn) + else + File.delete(fn) + end + end + end + return nil + end +end + diff --git a/code/keys.rb b/code/keys.rb new file mode 100644 index 00000000..0ce660a9 --- /dev/null +++ b/code/keys.rb @@ -0,0 +1,323 @@ +module Fm + # ALL combinations of multiple keys (without the last letter) + # or regexps which match combinations need to be in here! + COMBS = %w( + g d y c Z rmdi t + /[m`']/ /[f/!].*/ + /(cw|cd|mv).*/ + /m(k(d(i(r(.*)?)?)?)?)?/ + /r(e(n(a(m(e(.*)?)?)?)?)?)?/ + ) + + # Create a regular expression which detects these combos + ary = [] + for token in COMBS + if token =~ /^\/(.*)\/$/ + ary << $1 + elsif token.size > 0 + ary << token.each_char.map {|t| "(?:#{t}" }.join + + (')?' * (token.size - 1)) + ')' + end + end + REGX = Regexp.new('^(?:' + ary.uniq.join('|') + ')$') + + def self.ignore_keys_for(t) + @ignore_until = Time.now + t + end + + def self.search(str, offset=0, backwards=false) + rx = Regexp.new(str, Regexp::IGNORECASE) + + ary = @pwd.files.dup + ary.wrap(@pwd.pos + offset) + + ary.reverse! if backwards + + for f in ary + g = File.basename(f) + if g =~ rx + @pwd.pointed_file = f + break + end + end + end + + def self.hints(str) + rx = Regexp.new(str, Regexp::IGNORECASE) + + ary = @pwd.files.dup + ary.wrap(@pwd.pos) + + n = 0 + pointed = false + for f in ary + g = File.basename(f) + if g =~ rx + unless pointed + @pwd.pointed_file = f + pointed = true + end + n += 1 + end + end + + return n + end + + def self.remember_dir + @memory["`"] = @memory["'"] = @pwd.path + end + + def self.press(key) + return if @ignore_until and Time.now < @ignore_until + + @ignore_until = nil + + case @buffer << key + + when '' + closei + starti + + when 'j' + if @pwd.size == 0 + @pwd.pos = 0 + elsif @pwd.pos >= @pwd.size - 1 + @pwd.pos = @pwd.size - 1 + else + @pwd.pos += 1 + end + + when 's' + closei + system('clear') + system('ls', '--color=auto', '--group-directories-first') + system('bash') + @pwd.refresh + starti + + when 'J' + (lines/2).times { press 'j' } + + when 'K' + (lines/2).times { press 'k' } + + when 'cp', 'yy' + @copy = [currentfile] + + when 'n' + search(@search_string, 1) + + when 'x' + fork { + sleep 1 + Ncurses.ungetch(104) + } + + when 'N' + search(@search_string, 0, true) + + when 'fh' + @buffer.clear + press('h') + + when /^f(.+)$/ + str = $1 + if @buffer =~ /^(.*).$/ + @buffer = $1 + elsif str =~ /^\s?(.*)(L|;||)$/ + @buffer = '' + @search_string = $1 + press('l') if $2 == ';' or $2 == 'L' + else + test = hints(str) + if test == 1 + @buffer = '' + press('l') + ignore_keys_for 0.5 + elsif test == 0 + @buffer = '' + ignore_keys_for 1 + end + end + + when /^\/(.+)$/ + str = $1 + if @buffer =~ /^(.*).$/ + @buffer = $1 + elsif str =~ /^\s?(.*)(L|;||)$/ + @buffer = '' + @search_string = $1 + press('l') if $2 == ';' or $2 == 'L' + else + search(str) + end + + when /^mkdir(.*)$/ + str = $1 + if @buffer =~ /^(.*).$/ + @buffer = $1 + elsif str =~ /^\s?(.*)(|)$/ + @buffer = '' + if $2 == '' + closei + system('mkdir', $1) + starti + @pwd.refresh + end + end + + when /^!(.+)$/ + str = $1 + if @buffer =~ /^(.*).$/ + @buffer = $1 + elsif str =~ /^(\!?)(.*)(|)$/ + @buffer = '' + if $3 == '' + closei + system("bash", "-c", $2) + gets unless $1.empty? + starti + @pwd.refresh + end + end + + when /^cd(.+)$/ + str = $1 + if @buffer =~ /^(.*).$/ + @buffer = $1 + elsif str =~ /^\s?(.*)(|)$/ + @buffer = '' + if $2 == '' + remember_dir + enter_dir_safely($1) + end + end + + when /^(?:mv|cw|rename)(.+)$/ + str = $1 + if @buffer =~ /^(.*).$/ + @buffer = $1 + elsif str =~ /^\s?(.*)(|)$/ + @buffer = '' + if $2 == '' + closei + system('mv', '-v', currentfile, $1) + starti + @pwd.refresh + end + end + + when 'th' + OPTIONS['hidden'] ^= true + @pwd.refresh + + when 'rmdir' + cf = currentfile + if cf and File.exists?(cf) + if File.directory?(cf) + system('rm', '-r', cf) + @pwd.refresh + end + end + + when 'p' + unless @copy.empty? + closei + system('cp','-v',*(@copy+[@pwd.path])) + starti + @pwd.refresh + end + + when /^[`'](.)$/ + if dir = @memory[$1] and not @pwd.path == dir + remember_dir + enter_dir_safely(dir) + end + + when /^m(.)$/ + @memory[$1] = @pwd.path + + when 'gg' + @pwd.pos = 0 + + when 'dd' + new_path = move_to_trash(currentfile) + @copy = [new_path] if new_path + @pwd.refresh + + when 'dD' + cf = currentfile + if cf and File.exists?(cf) + if File.directory?(cf) + Dir.delete(cf) rescue nil + else + File.delete(cf) rescue nil + end + @pwd.refresh + end + + when 'g0' + remember_dir + enter_dir('/') + + when 'gh' + remember_dir + enter_dir('~') + + when 'gu' + remember_dir + enter_dir('/usr') + + when 'ge' + remember_dir + enter_dir('/etc') + + when 'gm' + remember_dir + enter_dir('/media') + + when 'gt' + remember_dir + enter_dir('~/.trash') + + when 'G' + @pwd.pos = @pwd.size - 1 + + when 'k' + @pwd.pos -= 1 + @pwd.pos = 0 if @pwd.pos < 0 + + when '', 'h', 'H' + enter_dir(@buffer=='H' ? '..' : @path[-2].path) unless @path.size == 1 + + when 'E' + cf = currentfile + unless cf.nil? or enter_dir_safely(cf) + closei + system VI % cf + starti + end + + when '', 'l', ';', 'L' + cf = currentfile + unless cf.nil? or enter_dir_safely(cf) + handler, wait = getfilehandler(currentfile) + if handler + closei + system(handler) + if @buffer == 'L' + gets + end + starti + end + end + + when 'q', 'ZZ', "\004" + exit + + end + + @buffer = '' unless @buffer == '' or @buffer =~ REGX + end +end diff --git a/code/old_fm.rb b/code/old_fm.rb new file mode 100644 index 00000000..6d868ebb --- /dev/null +++ b/code/old_fm.rb @@ -0,0 +1,233 @@ +class Directory + def initialize(path) + @path = path + @pos = 0 + refresh + end + + attr_reader(:path, :files) + attr_accessor(:pos) + + def refresh() + @files = Dir::glob(File::join(path, '*')).sort! + end + def self.current() + Fm.current_dir() + end +end + +module Fm + def self.initialize + @key = '' + @dirs = {} + @current_dir = '' + enter_dir(Dir.getwd) + end + + def self.current_path() self.current_dir.path end + + attr_reader(:dirs, :current_dir) + +# { +# "/" => [ 2, +# ["usr", "home", "root", "etc"] ], +# ... +# } + + + def self.getfilehandler(file) + bn = File.basename(file) + case bn + when /\.(avi|mpg|flv)$/ + "mplayer #{file} >> /dev/null" + when /\.(jpe?g|png)$/ + "feh '#{file}'" + when /\.m3u$/ + "cmus-remote -c && cmus-remote -P #{file} && cmus-remote -C 'set play_library=false' && sleep 0.3 && cmus-remote -n" + end + end + + def self.enter_dir(dir) + dir = File.expand_path(dir) + olddirs = @dirs.dup + @dirs = {} + + cur = 0 + got = "" + ary = dir.split('/') + if ary == []; ary = [''] end + ["", *ary].each do |folder| + got = File.join(got, folder) + cur = olddirs.has_key?(got) ? olddirs[got][0] : 0 + @dirs[got] = [cur, Dir.glob(File.join(got, '*')).sort] + end + + # quick fix, sets the cursor correctly when entering ".." + if @dirs.size < olddirs.size + @dirs[@currentdir] = olddirs[@currentdir] + @dirs[got][0] = @dirs[got][1].index(@currentdir) || 0 + end + +# log @dirs + + @currentdir = got +# @dirs[dir] = Dir[File.join(dir, '*')] + Dir.chdir(got) + +# log(@dirs) + end + + def self.cursor() @dirs[@currentdir][0] end + def self.cursor=(x) @dirs[@currentdir][0] = x end + + def self.currentdir() @dirs[@currentdir][1] end + + def self.currentfile + @dirs[@currentdir][1][@dirs[@currentdir][0] || 0] + end + + def self.get_offset(dir, max) + pos = dir[0] + len = dir[1].size + max -= 2 + if len <= max or pos < max/2 + return 0 + elsif pos > (len - max/2) + return len - max + else + return pos - max/2 + end + end + + def self.put_directory(c, d) + l = 0 + unless d == nil + offset = get_offset(d, lines) + (lines - 1).times do |l| + lpo = l + offset + break if (f = d[1][lpo]) == nil + + if File.symlink?(f) + color(3) + elsif File.directory?(f) + color(4) + elsif File.executable?(f) + color(2) + else + color(7) + end + puti l+1, c*@wid, File.basename(f).ljust(@wid-1)[0, @wid] + end + end + + column_clear(c, l) + end + + def self.column_clear(n, from=0) + (from -1).upto(lines) do |l| + puti l+2, (n * @wid), ' ' * @wid + end + end + + def self.column_put_file(n, file) + m = lines + i = 0 + File.open(file, 'r') do |f| + f.lines.each do |l| + puti i+1, n * @wid, l.gsub("\t"," ")[0, @wid-1].ljust(@wid-1) + i += 1 + break if i == m + end + end + column_clear(n, i) + end + + def self.draw + color 7 + puti 0, 3, "pwd: #{@current_path}".ljust(cols) + + if @dirs.size == 1 + @temp = [nil, @dirs["/"]] + else + left = @dirs[@currentdir[0, @currentdir.rindex('/')]] + left ||= @dirs['/'] + @temp = [left, @dirs[@currentdir]] + end + + @wid = cols / 3 + f = currentfile + begin + if File.directory?(f) + put_directory(2, [0, Dir.glob(File.join(f, '*')).sort]) + else + column_put_file(2, currentfile) + end + rescue + column_clear(2) + end + + 2.times do |c| + put_directory(c, @temp[c]) + end + + + movi(self.cursor + 1 - get_offset(@dirs[@currentdir], lines), @wid) + end + + # ALL combinations of multiple keys have to be in the COMBS array. + COMBS = %w( + gg + ) + def self.main_loop + while true + draw + + case @key << geti + when 'j' + self.cursor += 1 + self.cursor = currentdir.size - 1 if self.cursor >= currentdir.size + + when 'gg' + self.cursor = 0 + + when 'gh' + enter_dir('~') + + when 'G' + self.cursor = currentdir.size - 1 + + when 'k' + self.cursor -= 1 + self.cursor = 0 if self.cursor < 0 + + when '', 'h' + enter_dir('..') unless @dirs.size == 1 + + when '', 'l' + if File.directory?(currentfile || '') + begin + olddir = @currentdir + enter_dir(currentfile) + rescue Exception + enter_dir olddir + end + else + h = getfilehandler(currentfile) + h and system(h) + end + + when 'q' + break + end + + unless @key == '' or COMBS.select{ |x| + x.size != @key.size and x.size > @key.size + }.map{ |x| + x[0, @key.size] + }.include? @key + @key = '' + end + end + end +end + diff --git a/code/types.rb b/code/types.rb new file mode 100644 index 00000000..7fc614e9 --- /dev/null +++ b/code/types.rb @@ -0,0 +1,26 @@ +module Fm + def self.getfilehandler(file) + bn = File.basename(file) + case bn + when /\.(avi|mpe?g|flv|mkv|ogm|mov|mp4|wmv|vob|php|divx?|mp3|ogg)$/i + return "mplayer -fs #{file.sh}", false + when /\.(jpe?g|png)$/i + return "feh #{file.sh}", false + when /\.(pdf)$/i + return "evince #{file.sh}" + when /\.(txt)$/i + return VI % file.sh + when /\.wav$/i + return "aplay -q #{file.sh}" + when /\.m3u$/i + return "cmus-remote -c && cmus-remote -P #{file} && cmus-remote -C 'set play_library=false' && sleep 0.3 && cmus-remote -n", false + end + + if File.executable?(file) + return "#{file.sh}", true + end + + return VI % file.sh + end +end + diff --git a/fm b/fm new file mode 100755 index 00000000..7133192f --- /dev/null +++ b/fm @@ -0,0 +1,41 @@ +#!/usr/bin/ruby + +def File::resolve_symlink( path = __FILE__ ) + path = readlink(path) while symlink?(path) + expand_path(path) +end + +def require_from_here ( *list ) + require File.join( FM_DIR, *list ) +end + +$: << FM_DIR = File::dirname(File::resolve_symlink) + +require 'ftools' +require 'pp' + +require_from_here 'interface/ncurses.rb' +require_from_here 'code/fm.rb' +require_from_here 'code/keys.rb' +require_from_here 'code/types.rb' +require_from_here 'code/extensions.rb' +include Interface + +ERROR_STREAM = File.open('/tmp/errorlog', 'a') +def log(obj) + $stdout = ERROR_STREAM + pp obj + $stdout.flush + $stdout = STDOUT + obj +end + +END { + closei + Fm.dump + ERROR_STREAM.close +} + +Fm.initialize +Fm.main_loop + diff --git a/interface/ncurses.rb b/interface/ncurses.rb new file mode 100644 index 00000000..0d2659e4 --- /dev/null +++ b/interface/ncurses.rb @@ -0,0 +1,182 @@ +require 'ncurses' + +module Interface + def self.keytable(key) + case key + when 12 + '' + when ?\n + '' + when ?\b, Ncurses::KEY_BACKSPACE + '' + when ?\e + '' + when ?\t + '' + when 32 + ' ' + when 0..127 + key.chr + else + '' + end + end + +# def key c#{{{ +# case c +# when 12 +# :redraw +# when ?\n +# :enter +# when ?\b, Ncurses::KEY_BACKSPACE +# :backspace +# when 32 +# :space +# when ?\t +# :tab +# when Ncurses::KEY_BTAB +# :TAB +# when ?\e +# :escape +# when 0..127 +# c +# when Ncurses::KEY_F1..Ncurses::KEY_F30 +# ('F' + (c-Ncurses::KEY_F1+1).to_s).to_sym +# when Ncurses::KEY_HOME +# :home +# when Ncurses::KEY_END +# :end +# when Ncurses::KEY_RESIZE +# :resize +# when Ncurses::KEY_DC +# :delete +# when Ncurses::KEY_ENTER +# ?\n +# when Ncurses::KEY_RIGHT +# :right +# when Ncurses::KEY_LEFT +# :left +# when Ncurses::KEY_UP +# :up +# when Ncurses::KEY_DOWN +# :down +# when Ncurses::KEY_NPAGE +# :pagedown +# when Ncurses::KEY_PPAGE +# :pageup +# when Ncurses::KEY_IC +# :insert +# else +## c +# :error +# end +# end#}}} + + def self.included(this) + @@window = Ncurses.initscr + starti + end + + def starti + @@screen = Ncurses.stdscr + @@screen.keypad(true) + Ncurses.start_color + Ncurses.use_default_colors + +# Ncurses.cbreak + Ncurses.noecho + Ncurses.curs_set 0 + Ncurses.halfdelay(1000) + @@colortable = [] + end + + def closei + Ncurses.echo + Ncurses.cbreak + Ncurses.curs_set 1 + Ncurses.endwin + end + + def geti + Interface::keytable(Ncurses.getch) + end + + def set_title(x) +# closei + print "\e]2;#{x}\007" +# system('echo', '-n', '-e', '"\e2;' + x + '\007"') +# starti + end + + def lines + Ncurses.LINES + end + + def cols + Ncurses.COLS + end + + def movi(y=0, x=0) + y < 0 and y += lines + Ncurses.move(y, x) + end + + def puti *args + case args.size + when 1 + Ncurses.addstr(args[0].to_s) + when 2 + if (y = args[0]) < 0 then y += Ncurses.LINES end + Ncurses.mvaddstr(y, 0, args[1].to_s) + when 3 + if (y = args[0]) < 0 then y += Ncurses.LINES end + Ncurses.mvaddstr(y, args[1], args[2].to_s) + end + end + + def color(fg = -1, bg = -1) + Ncurses.color_set(get_color(fg,bg), nil) + end + + def color_at y, x=0, len=-1, fg=-1, bg=-1 + if y < 0 then y += Ncurses.LINES end + Ncurses.mvchgat(y, x, len, 0, get_color(fg, bg), nil) + end + + def color_bold_at y, x=0, len=-1, fg=-1, bg=-1 + if y < 0 then y += Ncurses.LINES end + Ncurses.mvchgat(y, x, len, Ncurses::A_BOLD, get_color(fg, bg), nil) + end + + def color_reverse_at y, x=0, len=-1, fg=-1, bg=-1 + if y < 0 then y += Ncurses.LINES end + Ncurses.mvchgat(y, x, len, Ncurses::A_REVERSE, get_color(fg, bg), nil) + end + + def get_color(fg, bg) + n = bg+2 + 9*(fg+2) + color = @@colortable[n] + unless color + # create a new pair + size = @@colortable.reject{|x| x.nil? }.size + 1 + Ncurses::init_pair(size, fg, bg) + color = @@colortable[n] = size + end + return color + end + + def bold(b = true) + if b + Ncurses.attron(Ncurses::A_BOLD) + else + Ncurses.attroff(Ncurses::A_BOLD) + end + end + def reverse(b = true) + if b + Ncurses.attron(Ncurses::A_REVERSE) + else + Ncurses.attroff(Ncurses::A_REVERSE) + end + end +end -- cgit 1.4.1-2-gfad0