summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/shared/settings.py223
1 files changed, 135 insertions, 88 deletions
diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py
index debe816d..054f5587 100644
--- a/ranger/shared/settings.py
+++ b/ranger/shared/settings.py
@@ -15,8 +15,9 @@
 
 import os
 import types
-from inspect import isclass, ismodule
+from inspect import isclass, ismodule, isfunction
 import ranger
+from ranger.ext.signal_dispatcher import SignalDispatcher
 from ranger.ext.openstruct import OpenStruct
 from ranger.gui.colorscheme import ColorScheme
 
@@ -40,7 +41,7 @@ ALLOWED_SETTINGS = {
 	'preview_files': bool,
 	'preview_directories': bool,
 	'flushinput': bool,
-	'colorscheme': str,
+	'colorscheme': (str, ColorScheme),
 	'colorscheme_overlay': (type(None), type(lambda:0)),
 	'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'),
 }
@@ -50,23 +51,140 @@ COMPAT_MAP = {
 	'sort_directories_first': 'directories_first',
 }
 
+
+class SettingObject(SignalDispatcher):
+	def __init__(self):
+		SignalDispatcher.__init__(self)
+		self.__dict__['_settings'] = dict()
+		self.__dict__['_setting_sources'] = list()
+		self.signal_bind('core.setting',
+				self._setting_set_raw_signal, priority=0.2)
+
+	def __setattr__(self, name, value):
+		if name[0] == '_':
+			self.__dict__[name] = value
+		else:
+			assert name in self._settings, "No such setting: {0}!".format(name)
+			assert self._check_type(name, value)
+			kws = dict(setting=name, value=value,
+					previous=self._settings[name])
+			self.signal_emit('core.setting', **kws)
+			self.signal_emit('core.setting.'+name, **kws)
+
+	def __getattr__(self, name):
+#		if name[0] == '_':
+#			return getattr(self, name)
+		assert name in ALLOWED_SETTINGS or name in self._settings, \
+				"No such setting: {0}!".format(name)
+		try:
+			return self._settings[name]
+		except:
+			for struct in self._setting_sources:
+				try: value = getattr(struct, name)
+				except: pass
+				else: break
+			else:
+				raise Exception("The option `{0}' was not defined" \
+						" in the defaults!".format(name))
+			assert self._check_type(name, value)
+			self._raw_set_setting(name, value)
+			self.__setattr__(name, value)
+			return self._settings[name]
+
+	def _check_type(self, name, value):
+		typ = ALLOWED_SETTINGS[name]
+		if isfunction(typ):
+			assert typ(value), \
+				"The option `" + name + "' has an incorrect type!"
+		else:
+			assert isinstance(value, typ), \
+				"The option `" + name + "' has an incorrect type!"\
+				" Got " + str(type(value)) + ", expected " + str(typ) + "!"
+		return True
+
+	__getitem__ = __getattr__
+	__setitem__ = __setattr__
+
+	def _raw_set_setting(self, name, value):
+		self._settings[name] = value
+
+	def _setting_set_raw_signal(self, signal):
+		self._settings[signal.setting] = signal.value
+
+def _colorscheme_name_to_class(signal):
+	# Find the colorscheme.  First look for it at ~/.ranger/colorschemes,
+	# then at RANGERDIR/colorschemes.  If the file contains a class
+	# named Scheme, it is used.  Otherwise, an arbitrary other class
+	# is picked.
+	if not signal.setting == 'colorscheme': return
+	if isinstance(signal.value, ColorScheme): return
+
+	scheme_name = signal.value
+	usecustom = not ranger.arg.clean
+
+	def exists(colorscheme):
+		return os.path.exists(colorscheme + '.py')
+
+	def is_scheme(x):
+		return isclass(x) and issubclass(x, ColorScheme)
+
+	# create ~/.ranger/colorschemes/__init__.py if it doesn't exist
+	if usecustom:
+		if os.path.exists(ranger.relpath_conf('colorschemes')):
+			initpy = ranger.relpath_conf('colorschemes', '__init__.py')
+			if not os.path.exists(initpy):
+				open(initpy, 'a').close()
+
+	if usecustom and \
+			exists(ranger.relpath_conf('colorschemes', scheme_name)):
+		scheme_supermodule = 'colorschemes'
+	elif exists(ranger.relpath('colorschemes', scheme_name)):
+		scheme_supermodule = 'ranger.colorschemes'
+	else:
+		scheme_supermodule = None  # found no matching file.
+
+	if scheme_supermodule is None:
+		# XXX: dont print while curses is running
+		print("ERROR: colorscheme not found, fall back to builtin scheme")
+		if ranger.arg.debug:
+			raise Exception("Cannot locate colorscheme!")
+		signal.value = ColorScheme()
+	else:
+		scheme_module = getattr(__import__(scheme_supermodule,
+				globals(), locals(), [scheme_name], 0), scheme_name)
+		assert ismodule(scheme_module)
+		if hasattr(scheme_module, 'Scheme') \
+				and is_scheme(scheme_module.Scheme):
+			signal.value = scheme_module.Scheme()
+		else:
+			for name, var in scheme_module.__dict__.items():
+				if var != ColorScheme and is_scheme(var):
+					signal.value = var()
+					break
+			else:
+				raise Exception("The module contains no valid colorscheme!")
+
+	# Making the colorscheme "SettingsAware" doesn't work because
+	# of circular imports, so we do it like this:
+	signal.value.settings = signal.origin
+
+
 # -- globalize the settings --
 class SettingsAware(object):
 	settings = OpenStruct()
 
 	@staticmethod
 	def _setup():
-		settings = OpenStruct()
+		settings = SettingObject()
+		settings.signal_bind('core.setting',
+				_colorscheme_name_to_class, priority=1)
 
-		from ranger.defaults import options
-		for setting in ALLOWED_SETTINGS:
-			try:
-				settings[setting] = getattr(options, setting)
-			except AttributeError:
-				raise Exception("The option `{0}' was not defined" \
-						" in the defaults!".format(setting))
+		from ranger.defaults import options as default_options
+		settings._setting_sources.append(default_options)
+		assert all(hasattr(default_options, setting) \
+				for setting in ALLOWED_SETTINGS), \
+				"Ensure that all options are defined in the defaults!"
 
-		import sys
 		if not ranger.arg.clean:
 			# overwrite single default options with custom options
 			try:
@@ -74,14 +192,12 @@ class SettingsAware(object):
 			except ImportError:
 				pass
 			else:
-				for setting in ALLOWED_SETTINGS:
-					try:
-						settings[setting] = getattr(my_options, setting)
-					except AttributeError:
-						pass
+				settings._setting_sources.append(my_options)
+
+				# For backward compatibility:
 				for new, old in COMPAT_MAP.items():
 					try:
-						settings[new] = getattr(my_options, old)
+						setattr(my_options, new, getattr(my_options, old))
 						print("Warning: the option `{0}'"\
 								" was renamed to `{1}'\nPlease update"\
 								" your configuration file soon." \
@@ -89,84 +205,15 @@ class SettingsAware(object):
 					except AttributeError:
 						pass
 
-		assert check_option_types(settings)
-
-		# Find the colorscheme.  First look for it at ~/.ranger/colorschemes,
-		# then at RANGERDIR/colorschemes.  If the file contains a class
-		# named Scheme, it is used.  Otherwise, an arbitrary other class
-		# is picked.
-
-		scheme_name = settings.colorscheme
-		usecustom = not ranger.arg.clean
-
-		def exists(colorscheme):
-			return os.path.exists(colorscheme + '.py')
-
-		def is_scheme(x):
-			return isclass(x) and issubclass(x, ColorScheme)
-
-		# create ~/.ranger/colorschemes/__init__.py if it doesn't exist
-		if usecustom:
-			if os.path.exists(ranger.relpath_conf('colorschemes')):
-				initpy = ranger.relpath_conf('colorschemes', '__init__.py')
-				if not os.path.exists(initpy):
-					open(initpy, 'a').close()
-
-		if usecustom and \
-				exists(ranger.relpath_conf('colorschemes', scheme_name)):
-			scheme_supermodule = 'colorschemes'
-		elif exists(ranger.relpath('colorschemes', scheme_name)):
-			scheme_supermodule = 'ranger.colorschemes'
-		else:
-			scheme_supermodule = None  # found no matching file.
-
-		if scheme_supermodule is None:
-			print("ERROR: colorscheme not found, fall back to builtin scheme")
-			if ranger.arg.debug:
-				raise Exception("Cannot locate colorscheme!")
-			settings.colorscheme = ColorScheme()
-		else:
-			scheme_module = getattr(__import__(scheme_supermodule,
-					globals(), locals(), [scheme_name], 0), scheme_name)
-			assert ismodule(scheme_module)
-			if hasattr(scheme_module, 'Scheme') \
-					and is_scheme(scheme_module.Scheme):
-				settings.colorscheme = scheme_module.Scheme()
-			else:
-				for name, var in scheme_module.__dict__.items():
-					if var != ColorScheme and is_scheme(var):
-						settings.colorscheme = var()
-						break
-				else:
-					raise Exception("The module contains no " \
-							"valid colorscheme!")
-
-		# Making the colorscheme SettingsAware doesn't work because
-		# of circular imports, so we do it like this:
-		settings.colorscheme.settings = settings  
-
 		try:
 			import apps
 		except ImportError:
 			from ranger.defaults import apps
-		settings.apps = apps
+		settings._raw_set_setting('apps', apps)
 		try:
 			import keys
 		except ImportError:
 			from ranger.defaults import keys
-		settings.keys = keys
+		settings._raw_set_setting('keys', keys)
 
 		SettingsAware.settings = settings
-
-def check_option_types(opt):
-	import inspect
-	for name, typ in ALLOWED_SETTINGS.items():
-		optvalue = getattr(opt, name)
-		if inspect.isfunction(typ):
-			assert typ(optvalue), \
-				"The option `" + name + "' has an incorrect type!"
-		else:
-			assert isinstance(optvalue, typ), \
-				"The option `" + name + "' has an incorrect type!"\
-				" Got " + str(type(optvalue)) + ", expected " + str(typ) + "!"
-	return True