summary refs log blame commit diff stats
path: root/code/fm/fm.rb
blob: 5033c16122ff9b7f3631ca878306ff2e2964069f (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                          




                                


         
                   

                          
                   

                                                              
        



                                    
                            

                                   


                                                     
                                 
 
                                                                            




                                   
                                

                                                   
                                                                          




                                                      
                                               

                                  
                                                                     
                                     
 










                                                      








                                    
 
                            
                                              
                               
 
                                                 
                                                               
                                       
                              


                                    

                              
                             

           
                 
                                       
           
 
                






                                              
                        



                            




                                                               
                     
                            
                          
                                                





                                                         
                             
                                      
                                        
                                            


                                           
                           
 
                             

                                          
                                        
                                            



                           
                                        
 
                                                           
 
                                 






                                                             


                                                   





                                                 
                          
                                    
                            















                                                                                  
 
                                 
                                   




                                                      
                             

                                            
                                                                               




                                               
                                  







                                                                

                                                  





                                     
 
                              

                                           
                   
                                                         
 
                                         
 
                               

           
                               

                              
                   
 

                                                         

                   








                                                                       
                            




                                                                      

                           
                          

           
                        






                                                            

                           
                            

           
                        



                                          
                   


           
OPTIONS = {
	'hidden' => false,
	'sort' => :name,
	'dir_first' => true,
	'sort_reverse' => false,
	'color' => true,
	'filepreview' => true,
}

module Fm
	extend self
	COPY_PRIORITY = -2

	COLUMNS = 4
	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 = nil
		@search_string = ''
		@copy = []
		@ignore_until = nil
		@trash = File.expand_path('~/.trash')
		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)) rescue nil
			if Hash === loaded
				@memory.update(loaded)
			end
		end

		# `0 is always the original PWD
		@memory['0'] = pwd

		# Give me some way to redraw screen while waiting for
		# input from CLI.geti

#		for i in 1..20
#			eval "Signal.trap(#{i}) do
#				log #{i}
#				exit if #{i} == 9 end"
#		end

		boot_up(pwd)
	end

	attr_reader(:dirs, :pwd)

	def pwd() @pwd end

	def refresh()
		begin
			@pwd.refresh
			draw
		rescue
		end
	end

	def boot_up(pwd=nil)
		pwd ||= @pwd.path || Dir.getwd
		Scheduler.reset

		@dirs = Hash.new() do |hash, key|
			hash[key] = newdir = Directory.new(key)
#			newdir.schedule
			newdir
		end

		@path = [@dirs['/']]
		enter_dir(pwd)

		Scheduler.run
	end

	def lines
		CLI::lines - @bars.size
	end

	def dump
		remember_dir
		dumped = Marshal.dump(@memory)
		File.open(@fmrc, 'w') do |f|
			f.write(dumped)
		end
	end

	def on_interrupt
		@buffer = ''
		sleep 0.2
	end

	def terminal_killed?
#		`ps ho tname --pid #{Process.pid}`.strip == '?'
		Process.ppid == 1
	end

	def main_loop
		bool = false
		while true
			exit if terminal_killed?
			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()
			rescue Interrupt
				on_interrupt
			rescue Exception
				log($!)
				log(caller)
			end

			begin
				key = geti
				press(key)
			rescue Interrupt
				on_interrupt
			end
		end
	end

	def current_path() @pwd.path end

	def reset_title() set_title("fm: #{@pwd.path}") end

	def 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
				log("NIGGER" * 100)
				log($!)
				log(caller)
				enter_dir(olddir)
				return false
			end
		end
	end

	def enter_dir(dir)
		@pwd.restore if @pwd
		@marked = []
		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
		@pwd.pos = @pwd.pos

		@pwd.files_raw.dup.each do |x|
			@dirs[x] if File.directory?(x)
		end

		reset_title()

		if @path.size < oldpath.size
			@pwd.pos = @pwd.files_raw.index(oldpath.last.path) || 0
		end

		i = 0

		@path.each_with_index do |p, i|
			p.schedule
			unless i == @path.size - 1
				p.pointed_file = @path[i+1].path
			end
		end

		Dir.chdir(@pwd.path)
	end

	def currentfile() @pwd.files[@pwd.pos] end
	def selection()
		if @marked.empty?
			[currentfile]
		else
			@marked.dup
		end
	end

	def move_to_trash!(fn)
		unless File.exists?(@trash)
			Dir.mkdir(@trash)
		end
		new_path = File.join(@trash, fn.basename)

		Action.move(fn, new_path)

		return new_path
	end

	def move_to_trash(file)
		unless file
			return
		end

		if String === file
			file = Directory::Entry.new(file)
		end

		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
				if !file.in?(@trash) and file.size > 0
					return move_to_trash!(file)
				else
					file.delete!
				end
			end
		end
		return nil
	end

	def 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 bar_del(bar)
		@bars.delete(bar)
		if @bars.empty?
			@bars_thread.kill
			@bars_thread = nil
		end
	end
end