summary refs log tree commit diff stats
path: root/ranger/shared/settings.py
diff options
context:
space:
mode:
Diffstat (limited to 'ranger/shared/settings.py')
-rw-r--r--ranger/shared/settings.py176
1 files changed, 96 insertions, 80 deletions
diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py
index cdddd623..a4a58e6e 100644
--- a/ranger/shared/settings.py
+++ b/ranger/shared/settings.py
@@ -13,22 +13,25 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import os
-import types
-from inspect import isclass, ismodule
 import ranger
+from ranger.ext.signal_dispatcher import SignalDispatcher
 from ranger.ext.openstruct import OpenStruct
-from ranger.gui.colorscheme import ColorScheme
 
 ALLOWED_SETTINGS = {
 	'show_hidden': bool,
 	'show_cursor': bool,
 	'autosave_bookmarks': bool,
+	'save_console_history': bool,
 	'collapse_preview': bool,
+	'column_ratios': (tuple, list, set),
+	'display_size_in_main_column': bool,
+	'display_size_in_status_bar': bool,
 	'draw_borders': bool,
+	'draw_bookmark_borders': bool,
 	'sort': str,
-	'reverse': bool,
-	'directories_first': bool,
+	'sort_reverse': bool,
+	'sort_case_insensitive': bool,
+	'sort_directories_first': bool,
 	'update_title': bool,
 	'shorten_title': int,  # Note: False is an instance of int
 	'max_filesize_for_preview': (int, type(None)),
@@ -38,26 +41,88 @@ ALLOWED_SETTINGS = {
 	'preview_directories': bool,
 	'flushinput': bool,
 	'colorscheme': str,
+	'colorscheme_overlay': (type(None), type(lambda:0)),
 	'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'),
 }
 
+
+COMPAT_MAP = {
+	'sort_reverse': 'reverse',
+	'sort_directories_first': 'directories_first',
+}
+
+
+class SettingObject(SignalDispatcher):
+	def __init__(self):
+		SignalDispatcher.__init__(self)
+		self.__dict__['_settings'] = dict()
+		self.__dict__['_setting_sources'] = list()
+
+	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_bind('setopt.'+name,
+					self._raw_set_with_signal, priority=0.2)
+			self.signal_emit('setopt', **kws)
+			self.signal_emit('setopt.'+name, **kws)
+
+	def __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(name, value)
+			self.__setattr__(name, value)
+			return self._settings[name]
+
+	def _check_type(self, name, value):
+		from inspect import isfunction
+		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(self, name, value):
+		self._settings[name] = value
+
+	def _raw_set_with_signal(self, signal):
+		self._settings[signal.setting] = signal.value
+
+
 # -- globalize the settings --
 class SettingsAware(object):
 	settings = OpenStruct()
 
 	@staticmethod
 	def _setup():
-		settings = OpenStruct()
+		settings = SettingObject()
 
-		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.gui.colorscheme import _colorscheme_name_to_class
+		settings.signal_bind('setopt.colorscheme',
+				_colorscheme_name_to_class, priority=1)
 
-		import sys
 		if not ranger.arg.clean:
 			# overwrite single default options with custom options
 			try:
@@ -65,83 +130,34 @@ class SettingsAware(object):
 			except ImportError:
 				pass
 			else:
-				for setting in ALLOWED_SETTINGS:
+				settings._setting_sources.append(my_options)
+
+				# For backward compatibility:
+				for new, old in COMPAT_MAP.items():
 					try:
-						settings[setting] = getattr(my_options, setting)
+						setattr(my_options, new, getattr(my_options, old))
+						print("Warning: the option `{0}'"\
+								" was renamed to `{1}'\nPlease update"\
+								" your configuration file soon." \
+								.format(old, new))
 					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
-
-		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 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 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!")
+		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!"
 
 		try:
 			import apps
 		except ImportError:
 			from ranger.defaults import apps
-		settings.apps = apps
+		settings._raw_set('apps', apps)
 		try:
 			import keys
 		except ImportError:
 			from ranger.defaults import keys
-		settings.keys = keys
+		settings._raw_set('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