diff options
Diffstat (limited to 'code/fm.rb')
-rw-r--r-- | code/fm.rb | 393 |
1 files changed, 153 insertions, 240 deletions
diff --git a/code/fm.rb b/code/fm.rb index 307866f8..e5b66aad 100644 --- a/code/fm.rb +++ b/code/fm.rb @@ -1,22 +1,40 @@ - OPTIONS = { 'hidden' => false, + 'sort' => :name, + 'dir_first' => true, + 'sort_reverse' => false, + 'color' => true, + 'filepreview' => true, } + module Fm - def self.initialize + SCHEDULED = [] + COLUMNS = 4 + UPDATE_SIGNAL = 31 + VI = "vi -c 'map h :quit<CR>' -c 'map q :quit<CR>'" << + " -c 'map H :unmap h<CR>:unmap H<CR>' %s" + + def self.initialize(pwd=nil) + @bars = [] + @bars_thread = nil + @buffer = '' - @pwd = '' +# @mutex = Mutex.new + @pwd = nil + @search_string = '' @copy = [] @ignore_until = nil @trash = File.expand_path('~/.trash') - pwd = Dir.getwd + pwd ||= Dir.getwd + # `' and `` are the original PWD unless overwritten by .fmrc @memory = { '`' => pwd, '\'' => pwd } + # Read the .fmrc @fmrc = File.expand_path('~/.fmrc') if (File.exists?(@fmrc)) loaded = Marshal.load(File.read(@fmrc)) @@ -25,18 +43,58 @@ module Fm end end + # `0 is always the original PWD @memory['0'] = pwd + # Give me some way to redraw screen while waiting for + # input from Interface.geti + Signal.trap(UPDATE_SIGNAL) do + @pwd.refresh + draw + end + + + # This thread inspects directories + @scheduler_active = false + @scheduler = Thread.new do + while true + Thread.stop + if @scheduler_active and !SCHEDULED.empty? +# @mutex.synchronize { + while dir = SCHEDULED.shift + dir.refresh(true) + dir.resize + force_update + end +# } + end + end + end + + @dirs = Hash.new() do |hash, key| - hash[key] = Directory.new(key) + hash[key] = newdir = Directory.new(key) + schedule newdir + newdir end @path = [@dirs['/']] - enter_dir(Dir.pwd) + enter_dir(pwd) + + @scheduler_active = true + @scheduler.run end + attr_reader(:dirs, :pwd) - VI = "vi -c 'map h :quit<CR>' -c 'map q :quit<CR>' -c 'map H :unmap h<CR>:unmap H<CR>' %s" + def self.force_update + # Send a signal to this process + Process.kill(UPDATE_SIGNAL, PID) + end + + def self.lines + Interface::lines - @bars.size + end def self.dump remember_dir @@ -46,29 +104,60 @@ module Fm end end - def self.rescue_me + def self.on_interrupt @buffer = '' sleep 0.2 end def self.main_loop while true + if @pwd.size == 0 or @pwd.pos < 0 + @pwd.pos = 0 + elsif @pwd.pos >= @pwd.size - 1 + @pwd.pos = @pwd.size - 1 + end + begin - draw() +# @mutex.synchronize { + draw() +# } rescue Interrupt - rescue_me + on_interrupt + rescue Exception +# log($!) +# log(caller) end + begin - press(geti) + key = geti +# @mutex.synchronize { + press(key) +# } rescue Interrupt - rescue_me + on_interrupt end end end def self.current_path() @pwd.path 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.enter_dir(dir) + @pwd.restore if @pwd + @marks = 0 dir = File.expand_path(dir) oldpath = @path.dup @@ -85,17 +174,23 @@ module Fm end end end + @pwd = @path.last + + @pwd.files_raw.dup.each do |x| + @dirs[x] if File.directory?(x) + end + set_title "fm: #{@pwd.path}" if @path.size < oldpath.size - @pwd.pos = @pwd.files.index(oldpath.last.path) || 0 + @pwd.pos = @pwd.files_raw.index(oldpath.last.path) || 0 end i = 0 @path.each_with_index do |p, i| - p.refresh + schedule(p) unless i == @path.size - 1 p.pointed_file = @path[i+1].path end @@ -104,254 +199,72 @@ module Fm 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.currentfile() @pwd.files[@pwd.pos] end - 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 + def self.schedule(dir) + SCHEDULED << dir + @scheduler.run 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 + def self.move_to_trash!(fn) + unless File.exists?(@trash) + Dir.mkdir(@trash) end + new_path = File.join(@trash, fn.basename) - column_clear(c, l) - end + Action.move(fn, new_path) - 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 + return new_path 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 + def self.move_to_trash(file) + unless file + return 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) + if String === file + file = Directory::Entry.new(file) end - pos_constant = @path.size - COLUMNS + 1 - - (COLUMNS - 1).times do |c| - pos = pos_constant + c - - if pos >= 0 - put_directory(c, @path[pos]) + if file.exists? + if file.dir? + if !file.in?(@trash) and file.size > 0 + return move_to_trash!(file) + else + Dir.rmdir(file.path) rescue nil + end + elsif file.symlink? + file.delete! else - column_clear(c) + if !file.in?(@trash) and file.size > 0 + return move_to_trash!(file) + else + file.delete! + end 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) + return nil 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 + def self.bar_add(bar) + if @bars.empty? + # This thread updates the statusbars + @bars_thread = Thread.new do + while true + draw_bars + sleep 0.5 + end end end + @bars << bar 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 + def self.bar_del(bar) + @bars.delete(bar) + if @bars.empty? + @bars_thread.kill + @bars_thread = nil end - return nil end end |