summary refs log tree commit diff stats
path: root/test/__init__.py
Commit message (Expand)AuthorAgeFilesLines
* updated / added testshut2009-12-131-0/+21
* clean upshut2009-12-111-0/+3
* run all tests with a single executablehut2009-12-111-0/+5
* implemented more stuffhut2009-11-251-0/+0
ef='#n51'>51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
# Copyright (C) 2009, 2010, 2011  Roman Zimbelmann <romanz@lavabit.com>
# This software is distributed under the terms of the GNU GPL version 3.

"""
The File Manager, putting the pieces together
"""

from time import time
from collections import deque
import mimetypes
import os.path
import pwd
import socket
import stat
import sys

import ranger
from ranger.core.actions import Actions
from ranger.container.tags import Tags
from ranger.gui.ui import UI
from ranger.container.bookmarks import Bookmarks
from ranger.core.runner import Runner
from ranger.ext.get_executables import get_executables
from ranger.ext.rifle import Rifle
from ranger.fsobject import Directory
from ranger.ext.signals import SignalDispatcher
from ranger import __version__
from ranger.core.loader import Loader

class FM(Actions, SignalDispatcher):
	input_blocked = False
	input_blocked_until = 0
	mode = 'normal'  # either 'normal' or 'visual'.
	search_method = 'ctime'

	_previous_selection = None
	_visual_reverse = False
	_visual_start = None
	_visual_start_pos = None

	def __init__(self, ui=None, bookmarks=None, tags=None):
		"""Initialize FM."""
		Actions.__init__(self)
		SignalDispatcher.__init__(self)
		if ui is None:
			self.ui = UI()
		else:
			self.ui = ui
		self.directories = dict()
		self.log = deque(maxlen=20)
		self.bookmarks = bookmarks
		self.tags = tags
		self.tabs = {}
		self.py3 = sys.version_info >= (3, )
		self.previews = {}
		self.current_tab = 1
		self.loader = Loader()
		self.copy_buffer = set()
		self.do_cut = False

		try:
			self.username = pwd.getpwuid(os.geteuid()).pw_name
		except:
			self.username = 'uid:' + str(os.geteuid())
		self.hostname = socket.gethostname()
		self.home_path = os.path.expanduser('~')

		self.log.append('ranger {0} started! Process ID is {1}.' \
				.format(__version__, os.getpid()))
		self.log.append('Running on Python ' + sys.version.replace('\n',''))

		mimetypes.knownfiles.append(os.path.expanduser('~/.mime.types'))
		mimetypes.knownfiles.append(self.relpath('data/mime.types'))
		self.mimetypes = mimetypes.MimeTypes()

	def initialize(self):
		"""If ui/bookmarks are None, they will be initialized here."""

		if not ranger.arg.clean and os.path.isfile(self.confpath('rifle.conf')):
			rifleconf = self.confpath('rifle.conf')
		else:
			rifleconf = self.relpath('config/rifle.conf')
		self.rifle = Rifle(rifleconf)
		self.rifle.reload_config()

		if self.bookmarks is None:
			if ranger.arg.clean:
				bookmarkfile = None
			else:
				bookmarkfile = self.confpath('bookmarks')
			self.bookmarks = Bookmarks(
					bookmarkfile=bookmarkfile,
					bookmarktype=Directory,
					autosave=self.settings.autosave_bookmarks)
			self.bookmarks.load()

		if not ranger.arg.clean and self.tags is None:
			self.tags = Tags(self.confpath('tagged'))

		self.ui.setup_curses()
		self.ui.initialize()

		self.rifle.hook_before_executing = lambda a, b, flags: \
			self.ui.suspend() if 'f' not in flags else None
		self.rifle.hook_after_executing = lambda a, b, flags: \
			self.ui.initialize() if 'f' not in flags else None
		self.rifle.hook_logger = self.notify

		def mylogfunc(text):
			self.notify(text, bad=True)
		self.run = Runner(ui=self.ui, logfunc=mylogfunc, fm=self)

		self.env.signal_bind('cd', self._update_current_tab)

		if self.settings.init_function:
			self.settings.init_function(self)

	def destroy(self):
		debug = ranger.arg.debug
		if self.ui:
			try:
				self.ui.destroy()
			except:
				if debug:
					raise
		if self.loader:
			try:
				self.loader.destroy()
			except:
				if debug:
					raise

	def block_input(self, sec=0):
		self.input_blocked = sec != 0
		self.input_blocked_until = time() + sec

	def input_is_blocked(self):
		if self.input_blocked and time() > self.input_blocked_until:
			self.input_blocked = False
		return self.input_blocked

	def copy_config_files(self, which):
		if ranger.arg.clean:
			sys.stderr.write("refusing to copy config files in clean mode\n")
			return
		import shutil
		def copy(_from, to):
			if os.path.exists(self.confpath(to)):
				sys.stderr.write("already exists: %s\n" % self.confpath(to))
			else:
				sys.stderr.write("creating: %s\n" % self.confpath(to))
				try:
					shutil.copy(self.relpath(_from), self.confpath(to))
				except Exception as e:
					sys.stderr.write("  ERROR: %s\n" % str(e))
		if which == 'rifle' or which == 'all':
			copy('config/rifle.conf', 'rifle.conf')
		if which == 'commands' or which == 'all':
			copy('config/commands.py', 'commands.py')
		if which == 'rc' or which == 'all':
			copy('config/rc.conf', 'rc.conf')
		if which == 'options' or which == 'all':
			copy('config/options.py', 'options.py')
		if which == 'scope' or which == 'all':
			copy('data/scope.sh', 'scope.sh')
			os.chmod(self.confpath('scope.sh'),
				os.stat(self.confpath('scope.sh')).st_mode | stat.S_IXUSR)
		if which not in \
				('all', 'rifle', 'scope', 'commands', 'rc', 'options'):
			sys.stderr.write("Unknown config file `%s'\n" % which)

	def confpath(self, *paths):
		"""returns the path relative to rangers configuration directory"""
		if ranger.arg.clean:
			assert 0, "Should not access relpath_conf in clean mode!"
		else:
			return os.path.join(ranger.arg.confdir, *paths)

	def relpath(self, *paths):
		"""returns the path relative to rangers library directory"""
		return os.path.join(ranger.RANGERDIR, *paths)

	def get_directory(self, path):
		"""Get the directory object at the given path"""
		path = os.path.abspath(path)
		try:
			return self.directories[path]
		except KeyError:
			obj = Directory(path)
			self.directories[path] = obj
			return obj

	def garbage_collect(self, age, tabs):
		"""Delete unused directory objects"""
		for key in tuple(self.directories):
			value = self.directories[key]
			if age != -1:
				if not value.is_older_than(age) or value in self.pathway:
					continue
				if value in tabs.values():
					continue
			del self.directories[key]
			if value.is_directory:
				value.files = None
		self.settings.signal_garbage_collect()
		self.signal_garbage_collect()

	def loop(self):
		"""
		The main loop consists of:
		1. reloading bookmarks if outdated
		2. letting the loader work
		3. drawing and finalizing ui
		4. reading and handling user input
		5. after X loops: collecting unused directory objects
		"""

		self.env.enter_dir(self.env.path)

		gc_tick = 0

		# for faster lookup:
		ui = self.ui
		throbber = ui.throbber
		loader = self.loader
		has_throbber = hasattr(ui, 'throbber')
		zombies = self.run.zombies

		try:
			while True:
				loader.work()
				if has_throbber:
					if loader.has_work():
						throbber(loader.status)
					else:
						throbber(remove=True)

				ui.redraw()

				ui.set_load_mode(not loader.paused and loader.has_work())

				ui.handle_input()

				if zombies:
					for zombie in tuple(zombies):
						if zombie.poll() is not None:
							zombies.remove(zombie)

				gc_tick += 1
				if gc_tick > ranger.TICKS_BEFORE_COLLECTING_GARBAGE:
					gc_tick = 0
					self.garbage_collect(
						ranger.TIME_BEFORE_FILE_BECOMES_GARBAGE, self.tabs)

		except KeyboardInterrupt:
			# this only happens in --debug mode. By default, interrupts
			# are caught in curses_interrupt_handler
			raise SystemExit

		finally:
			if ranger.arg.choosedir and self.env.cwd and self.env.cwd.path:
				# XXX: UnicodeEncodeError: 'utf-8' codec can't encode character
				# '\udcf6' in position 42: surrogates not allowed
				open(ranger.arg.choosedir, 'w').write(self.env.cwd.path)
			self.bookmarks.remember(self.env.cwd)
			self.bookmarks.save()