summary refs log blame commit diff stats
path: root/ranger/gui/widgets/browserview.py
blob: e4ab2166e9c2ba5d9f6c49c78f62196259b28862 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                       
                                                                        
 
                                                      
                      
                                     
                                                      
                    
                                        
                        

                                              
                                                

                      
                            
                              
                             
                          
                            
                          
                         


                                                        
                                      





                                                           
                                          




                                                                                      
                                                               
                                                                                     
 
                                        






                                                 
 
                                              
                                                                  
 
                                                                
                                         
                                                                  

                                                                                          
 
                                        
                                            

                                                
                                                                    
                                          
                                               

                    
                                                                                  
                                  
                                               
                     
                                                                  
                                                           
 
                                                               






                                                

                       



                                               

                                                 


                                                                    


                                               

                                              

                                          

                                                       


                                      



                                                                                           
                                               
                     
                            


                                                                                         


                                                         
                                                     
 




















                                                                






                                                                                          





















                                                                                       
                                  
                                                      
                                  

                                      


                                                                               
 




                                                                                   
 
                                         
                                                                            





                                                                        
 












                                                          


                                      
                                                                 
                                            












                                                                                    



                                                                         







                                                                

                                                                                  




                                                                           









                                                                                                      

                                          
 
                                         
                                                                         
                                                                 


                                                    
 

                                                    
                                                                  
                     
                                                          
 

                                             
                                          
                                                   
 
                                                                                       
                                       

                                                                            



                                                                                               
 
                                           
                                                                             
                                                                              
 




                                                                                          
                            
                                                                                  
                                                                

                                        
 
                                   
 
                               


                                                           

                                                          
                            
 
                             

                                         
                                      

                                 

                                                        

                                  
 
                              

                                          
                                      

                                  

                                                       

                                  
 

                                               




                                                                                  
                                                                             

                                                       

                                                                          
# Copyright (C) 2009, 2010, 2011  Roman Zimbelmann <romanz@lavabit.com>
# This software is distributed under the terms of the GNU GPL version 3.

"""The BrowserView manages a set of BrowserColumns."""
import curses, _curses
from ranger.ext.signals import Signal
from ranger.ext.keybinding_parser import key_to_string
from . import Widget
from .browsercolumn import BrowserColumn
from .pager import Pager
from ..displayable import DisplayableContainer

class BrowserView(Widget, DisplayableContainer):
	ratios = None
	preview = True
	is_collapsed = False
	draw_bookmarks = False
	stretch_ratios = None
	need_clear = False
	old_collapse = False
	draw_hints = False
	draw_info = False

	def __init__(self, win, ratios, preview = True):
		DisplayableContainer.__init__(self, win)
		self.preview = preview
		self.columns = []

		self.pager = Pager(self.win, embedded=True)
		self.pager.visible = False
		self.add_child(self.pager)

		self.change_ratios(ratios)

		for option in ('preview_directories', 'preview_files'):
			self.settings.signal_bind('setopt.' + option,
					self._request_clear_if_has_borders, weak=True)

		self.fm.signal_bind('move', self.request_clear)
		self.settings.signal_bind('setopt.column_ratios', self.request_clear)

	def change_ratios(self, ratios):
		if isinstance(ratios, Signal):
			ratios = ratios.value

		for column in self.columns:
			column.destroy()
			self.remove_child(column)
		self.columns = []

		ratio_sum = float(sum(ratios))
		self.ratios = tuple(x / ratio_sum for x in ratios)

		last = 0.1 if self.settings.padding_right else 0
		if len(self.ratios) >= 2:
			self.stretch_ratios = self.ratios[:-2] + \
					((self.ratios[-2] + self.ratios[-1] * 1.0 - last),
					(self.ratios[-1] * last))

		offset = 1 - len(ratios)
		if self.preview: offset += 1

		for level in range(len(ratios)):
			fl = BrowserColumn(self.win, level + offset)
			self.add_child(fl)
			self.columns.append(fl)

		try:
			self.main_column = self.columns[self.preview and -2 or -1]
		except IndexError:
			self.main_column = None
		else:
			self.main_column.display_infostring = True
			self.main_column.main_column = True

		self.resize(self.y, self.x, self.hei, self.wid)

	def _request_clear_if_has_borders(self):
		if self.settings.draw_borders:
			self.request_clear()

	def request_clear(self):
		self.need_clear = True

	def draw(self):
		if self.need_clear:
			self.win.erase()
			self.need_redraw = True
			self.need_clear = False
		for tab in self.fm.tabs.values():
			directory = tab.thisdir
			if directory:
				directory.load_content_if_outdated()
				directory.use()
		DisplayableContainer.draw(self)
		if self.settings.draw_borders:
			self._draw_borders()
		if self.draw_bookmarks:
			self._draw_bookmarks()
		elif self.draw_hints:
			self._draw_hints()
		elif self.draw_info:
			self._draw_info(self.draw_info)

	def finalize(self):
		if self.pager.visible:
			try:
				self.fm.ui.win.move(self.main_column.y, self.main_column.x)
			except:
				pass
			self.pager.draw_image()
		else:
			try:
				x = self.main_column.x
				y = self.main_column.y + self.main_column.target.pointer\
						- self.main_column.scroll_begin
				self.fm.ui.win.move(y, x)
			except:
				pass
			self.columns[-1].draw_image()

	def _draw_borders(self):
		win = self.win
		self.color('in_browser', 'border')

		left_start = 0
		right_end = self.wid - 1

		for child in self.columns:
			if not child.has_preview():
				left_start = child.x + child.wid
			else:
				break
		if not self.pager.visible:
			for child in reversed(self.columns):
				if not child.has_preview():
					right_end = child.x - 1
				else:
					break
			if right_end < left_start:
				right_end = self.wid - 1

		try:
			win.hline(0, left_start, curses.ACS_HLINE, right_end - left_start)
			win.hline(self.hei - 1, left_start, curses.ACS_HLINE,
					right_end - left_start)
			win.vline(1, left_start, curses.ACS_VLINE, self.hei - 2)
		except _curses.error:
			pass

		for child in self.columns:
			if not child.has_preview():
				continue
			if child.main_column and self.pager.visible:
				win.vline(1, right_end, curses.ACS_VLINE, self.hei - 2)
				break
			x = child.x + child.wid
			y = self.hei - 1
			try:
				win.vline(1, x, curses.ACS_VLINE, y - 1)
				win.addch(0, x, curses.ACS_TTEE, 0)
				win.addch(y, x, curses.ACS_BTEE, 0)
			except:
				# in case it's off the boundaries
				pass

		self.addch(0, left_start, curses.ACS_ULCORNER)
		self.addch(self.hei - 1, left_start, curses.ACS_LLCORNER)
		self.addch(0, right_end, curses.ACS_URCORNER)
		self.addch(self.hei - 1, right_end, curses.ACS_LRCORNER)

	def _draw_bookmarks(self):
		self.fm.bookmarks.update_if_outdated()
		self.color_reset()
		self.need_clear = True

		sorted_bookmarks = sorted((item for item in self.fm.bookmarks \
			if self.fm.settings.show_hidden_bookmarks or \
			'/.' not in item[1].path), key=lambda t: t[0].lower())

		hei = min(self.hei - 1, len(sorted_bookmarks))
		ystart = self.hei - hei

		maxlen = self.wid
		self.addnstr(ystart - 1, 0, "mark  path".ljust(self.wid), self.wid)

		whitespace = " " * maxlen
		for line, items in zip(range(self.hei-1), sorted_bookmarks):
			key, mark = items
			string = " " + key + "   " + mark.path
			self.addstr(ystart + line, 0, whitespace)
			self.addnstr(ystart + line, 0, string, self.wid)

		self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)

	def _draw_info(self, lines):
		self.need_clear = True
		hei = min(self.hei - 1, len(lines))
		ystart = self.hei - hei
		i = ystart
		whitespace = " " * self.wid
		for line in lines:
			if i >= self.hei:
				break
			self.addstr(i, 0, whitespace)
			self.addnstr(i, 0, line, self.wid)
			i += 1

	def _draw_hints(self):
		self.need_clear = True
		hints = []
		for k, v in self.fm.ui.keybuffer.pointer.items():
			k = key_to_string(k)
			if isinstance(v, dict):
				text = '...'
			else:
				text = v
			if text.startswith('hint') or text.startswith('chain hint'):
				continue
			hints.append((k, text))
		hints.sort(key=lambda t: t[1])

		hei = min(self.hei - 1, len(hints))
		ystart = self.hei - hei
		self.addnstr(ystart - 1, 0, "key          command".ljust(self.wid),
				self.wid)
		try:
			self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
		except:
			pass
		whitespace = " " * self.wid
		i = ystart
		for key, cmd in hints:
			string = " " + key.ljust(11) + " " + cmd
			self.addstr(i, 0, whitespace)
			self.addnstr(i, 0, string, self.wid)
			i += 1

	def _collapse(self):
		# Should the last column be cut off? (Because there is no preview)
		if not self.settings.collapse_preview or not self.preview \
				or not self.stretch_ratios:
			return False
		result = not self.columns[-1].has_preview()
		target = self.columns[-1].target
		if not result and target and target.is_file:
			if target.image and self.fm.settings.preview_images:
				result = False  # don't collapse when drawing images
			elif self.fm.settings.preview_script and \
					self.fm.settings.use_preview_script:
				try:
					result = not self.fm.previews[target.realpath]['foundpreview']
				except:
					return self.old_collapse

		self.old_collapse = result
		return result

	def resize(self, y, x, hei, wid):
		"""Resize all the columns according to the given ratio"""
		DisplayableContainer.resize(self, y, x, hei, wid)
		borders = self.settings.draw_borders
		pad = 1 if borders else 0
		left = pad

		self.is_collapsed = self._collapse()
		if self.is_collapsed:
			generator = enumerate(self.stretch_ratios)
		else:
			generator = enumerate(self.ratios)

		last_i = len(self.ratios) - 1

		for i, ratio in generator:
			wid = int(ratio * self.wid)

			cut_off = self.is_collapsed and not self.settings.padding_right
			if i == last_i:
				if not cut_off:
					wid = int(self.wid - left + 1 - pad)
				else:
					self.columns[i].resize(pad, left - 1, hei - pad * 2, 1)
					self.columns[i].visible = False
					continue

			if i == last_i - 1:
				self.pager.resize(pad, left, hei - pad * 2, \
						max(1, self.wid - left - pad))

				if cut_off:
					self.columns[i].resize(pad, left, hei - pad * 2, \
							max(1, self.wid - left - pad))
					continue

			try:
				self.columns[i].resize(pad, left, hei - pad * 2, \
						max(1, wid - 1))
			except KeyError:
				pass

			left += wid

	def click(self, event):
		if DisplayableContainer.click(self, event):
			return True
		direction = event.mouse_wheel_direction()
		if direction:
			self.main_column.scroll(direction)
		return False

	def open_pager(self):
		self.pager.visible = True
		self.pager.focused = True
		self.need_clear = True
		self.pager.open()
		try:
			self.columns[-1].visible = False
			self.columns[-2].visible = False
		except IndexError:
			pass

	def close_pager(self):
		self.pager.visible = False
		self.pager.focused = False
		self.need_clear = True
		self.pager.close()
		try:
			self.columns[-1].visible = True
			self.columns[-2].visible = True
		except IndexError:
			pass

	def poke(self):
		DisplayableContainer.poke(self)

		# Show the preview column when it has a preview but has
		# been hidden (e.g. because of padding_right = False)
		if not self.pager.visible and not self.columns[-1].visible and \
		self.columns[-1].target and self.columns[-1].target.is_directory \
		or self.columns[-1].has_preview() and not self.pager.visible:
			self.columns[-1].visible = True

		if self.preview and self.is_collapsed != self._collapse():
			self.resize(self.y, self.x, self.hei, self.wid)