terminator/terminatorlib/config.py

354 lines
14 KiB
Python
Executable File

#!/usr/bin/python
# TerminatorConfig - layered config classes
# Copyright (C) 2006-2008 cmsj@tenshu.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2 only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""Terminator by Chris Jones <cmsj@tenshu.net>
Classes relating to configuration
>>> DEFAULTS['global_config']['focus']
'click'
>>> config = Config()
>>> config['focus'] = 'sloppy'
>>> config['focus']
'sloppy'
>>> DEFAULTS['global_config']['focus']
'click'
>>> config2 = Config()
>>> config2['focus']
'sloppy'
>>> config2['focus'] = 'click'
>>> config2['focus']
'click'
>>> config['focus']
'click'
>>>
"""
import platform
import os
import sys
from copy import copy
from configobj import ConfigObj
from borg import Borg
from factory import Factory
from util import dbg, get_config_dir, dict_diff
DEFAULTS = {
'global_config': {
'focus' : 'click',
'enable_real_transparency' : True,
'handle_size' : -1,
'geometry_hinting' : True,
'fullscreen' : False,
'borderless' : False,
'maximise' : False,
'hidden' : False,
'tab_position' : 'top',
'close_button_on_tab' : True,
'hide_tabbar' : False,
'scroll_tabbar' : False,
'try_posix_regexp' : platform.system() != 'Linux',
},
'keybindings': {
'zoom_in' : '<Ctrl>plus',
'zoom_out' : '<Ctrl>minus',
'zoom_normal' : '<Ctrl>0',
'new_root_tab' : '<Ctrl><Shift><Alt>T',
'new_tab' : '<Ctrl><Shift>T',
'go_next' : ('<Ctrl><Shift>N','<Ctrl>Tab'),
'go_prev' : ('<Ctrl><Shift>P','<Ctrl><Shift>Tab'),
'go_up' : '<Alt>Up',
'go_down' : '<Alt>Down',
'go_left' : '<Alt>Left',
'go_right' : '<Alt>Right',
'split_horiz' : '<Ctrl><Shift>O',
'split_vert' : '<Ctrl><Shift>E',
'close_term' : '<Ctrl><Shift>W',
'copy' : '<Ctrl><Shift>C',
'paste' : '<Ctrl><Shift>V',
'toggle_scrollbar' : '<Ctrl><Shift>S',
'search' : '<Ctrl><Shift>F',
'close_window' : '<Ctrl><Shift>Q',
'resize_up' : '<Ctrl><Shift>Up',
'resize_down' : '<Ctrl><Shift>Down',
'resize_left' : '<Ctrl><Shift>Left',
'resize_right' : '<Ctrl><Shift>Right',
'move_tab_right' : '<Ctrl><Shift>Page_Down',
'move_tab_left' : '<Ctrl><Shift>Page_Up',
'toggle_zoom' : '<Ctrl><Shift>X',
'scaled_zoom' : '<Ctrl><Shift>Z',
'next_tab' : '<Ctrl>Page_Down',
'prev_tab' : '<Ctrl>Page_Up',
'switch_to_tab_1' : None,
'switch_to_tab_2' : None,
'switch_to_tab_3' : None,
'switch_to_tab_4' : None,
'switch_to_tab_5' : None,
'switch_to_tab_6' : None,
'switch_to_tab_7' : None,
'switch_to_tab_8' : None,
'switch_to_tab_9' : None,
'switch_to_tab_10' : None,
'full_screen' : 'F11',
'reset' : '<Ctrl><Shift>R',
'reset_clear' : '<Ctrl><Shift>G',
'hide_window' : '<Ctrl><Shift><Alt>a',
'group_all' : '<Super>g',
'ungroup_all' : '<Super><Shift>g',
'group_tab' : '<Super>t',
'ungroup_tab' : '<Super><Shift>T',
'new_window' : '<Ctrl><Shift>I',
},
'profiles': {
'default': {
'titlebars' : True,
'zoomedtitlebar' : True,
'allow_bold' : True,
'audible_bell' : False,
'visible_bell' : True,
'urgent_bell' : False,
'background_color' : '#000000',
'background_darkness' : 0.5,
'background_type' : 'solid',
'background_image' : '',
'backspace_binding' : 'ascii-del',
'delete_binding' : 'delete-sequence',
'cursor_blink' : True,
'cursor_shape' : 'block',
'cursor_color' : '',
'emulation' : 'xterm',
'font' : 'Mono 10',
'foreground_color' : '#AAAAAA',
'scrollbar_position' : "right",
'scroll_background' : True,
'scroll_on_keystroke' : True,
'scroll_on_output' : True,
'scrollback_lines' : 500,
'exit_action' : 'close',
'palette' :'#000000000000:#CDCD00000000:#0000CDCD0000:\
#CDCDCDCD0000:#30BF30BFA38E:#A53C212FA53C:\
#0000CDCDCDCD:#FAFAEBEBD7D7:#404040404040:\
#FFFF00000000:#0000FFFF0000:#FFFFFFFF0000:\
#00000000FFFF:#FFFF0000FFFF:#0000FFFFFFFF:\
#FFFFFFFFFFFF',
'word_chars' : '-A-Za-z0-9,./?%&#:_',
'mouse_autohide' : True,
'update_records' : True,
'login_shell' : False,
'use_custom_command' : False,
'custom_command' : '',
'use_system_font' : True,
'use_theme_colors' : False,
'encoding' : 'UTF-8',
'active_encodings' : ['UTF-8', 'ISO-8859-1'],
'focus_on_close' : 'auto',
'force_no_bell' : False,
'cycle_term_tab' : True,
'copy_on_selection' : False,
'title_tx_txt_color' : '#FFFFFF',
'title_tx_bg_color' : '#C80003',
'title_rx_txt_color' : '#FFFFFF',
'title_rx_bg_color' : '#0076C9',
'title_ia_txt_color' : '#000000',
'title_ia_bg_color' : '#C0BEBF',
'alternate_screen_scroll': True,
'split_to_group' : False,
'autoclean_groups' : True,
'http_proxy' : '',
'ignore_hosts' : ['localhost','127.0.0.0/8','*.local'],
},
},
'layouts': {
},
'plugins': {
},
}
class Config(object):
"""Class to provide a slightly richer config API above ConfigBase"""
base = None
profile = None
def __init__(self, profile='default'):
self.base = ConfigBase()
self.profile = profile
def __getitem__(self, key):
"""Look up a configuration item"""
return(self.base.get_item(key, self.profile))
def __setitem__(self, key, value):
"""Set a particular configuration item"""
return(self.base.set_item(key, value, self.profile))
def set_profile(self, profile):
"""Set our profile (which usually means change it)"""
self.profile = profile
if not self.base.profiles.has_key(profile):
self.base.profiles[profile] = copy(DEFAULTS['profiles']['default'])
def save(self):
"""Cause ConfigBase to save our config to file"""
return(self.base.save())
class ConfigBase(Borg):
"""Class to provide access to our user configuration"""
loaded = None
sections = None
global_config = None
profiles = None
keybindings = None
plugins = None
layouts = None
def __init__(self):
"""Class initialiser"""
Borg.__init__(self, self.__class__.__name__)
self.prepare_attributes()
self.load()
def prepare_attributes(self):
"""Set up our borg environment"""
if self.loaded is None:
self.loaded = False
if self.sections is None:
self.sections = ['global_config', 'keybindings', 'profiles',
'layouts', 'plugins']
if self.global_config is None:
self.global_config = copy(DEFAULTS['global_config'])
if self.profiles is None:
self.profiles = {}
self.profiles['default'] = copy(DEFAULTS['profiles']['default'])
if self.keybindings is None:
self.keybindings = copy(DEFAULTS['keybindings'])
if self.plugins is None:
self.plugins = {}
if self.layouts is None:
self.layouts = copy(DEFAULTS['layouts'])
def load(self):
"""Load configuration data from our various sources"""
if self.loaded is True:
dbg('ConfigBase::load: config already loaded')
return
filename = os.path.join(get_config_dir(), 'epic-config')
try:
configfile = open(filename, 'r')
except Exception, ex:
dbg('ConfigBase::load: Unable to open %s (%s)' % (filename, ex))
return
parser = ConfigObj(configfile)
for section_name in self.sections:
dbg('ConfigBase::load: Processing section: %s' % section_name)
section = getattr(self, section_name)
if section_name == 'profiles':
for profile in parser[section_name]:
dbg('ConfigBase::load: Processing profile: %s' % profile)
if not section.has_key(section_name):
section[profile] = copy(DEFAULTS['profiles']['default'])
section[profile].update(parser[section_name][profile])
elif section_name == ['layouts', 'plugins']:
for part in parser[section_name]:
dbg('ConfigBase::load: Processing %s: %s' % (section_name,
part))
section[part] = parser[section_name][part]
else:
try:
section.update(parser[section_name])
except KeyError, ex:
dbg('ConfigBase::load: skipping loading missing section %s' %
section_name)
self.loaded = True
def save(self):
"""Save the config to a file"""
dbg('ConfigBase::save: saving config')
parser = ConfigObj()
parser.indent_type = ' '
for section_name in ['global_config', 'keybindings']:
dbg('ConfigBase::save: Processing section: %s' % section_name)
section = getattr(self, section_name)
parser[section_name] = dict_diff(DEFAULTS[section_name], section)
parser['profiles'] = {}
for profile in self.profiles:
dbg('ConfigBase::save: Processing profile: %s' % profile)
parser['profiles'][profile] = dict_diff(DEFAULTS['profiles']['default'],
self.profiles[profile])
parser['layouts'] = {}
for layout in self.layouts:
dbg('ConfigBase::save: Processing layout: %s' % layout)
parser['layouts'][layout] = self.layouts[layout]
parser['plugins'] = {}
for plugin in self.plugins:
dbg('ConfigBase::save: Processing plugin: %s' % plugin)
parser['plugins'][plugin] = self.plugins[plugin]
parser.write(open(os.path.join(get_config_dir(), 'epic-config'), 'w'))
def get_item(self, key, profile='default', plugin=None):
"""Look up a configuration item"""
dbg('ConfigBase::get_item: %s:%s' % (profile, key))
if self.global_config.has_key(key):
dbg('ConfigBase::get_item: found in globals: %s' %
self.global_config[key])
return(self.global_config[key])
elif self.profiles[profile].has_key(key):
dbg('ConfigBase::get_item: found in profile %s (%s)' % (
profile, self.profiles[profile][key]))
return(self.profiles[profile][key])
elif key == 'keybindings':
return(self.keybindings)
elif plugin is not None and self.plugins[plugin].has_key(key):
dbg('ConfigBase::get_item: found in plugin %s (%s)' % (
plugin, self.plugins[plugin][key]))
return(self.plugins[plugin][key])
else:
raise KeyError('ConfigBase::get_item: unknown key %s' % key)
def set_item(self, key, value, profile='default', plugin=None):
"""Set a configuration item"""
dbg('ConfigBase::set_item: Setting %s=%s (profile=%s, plugin=%s)' %
(key, value, profile, plugin))
if self.global_config.has_key(key):
self.global_config[key] = value
elif self.profiles[profile].has_key(key):
self.profiles[profile][key] = value
elif key == 'keybindings':
self.keybindings = value
elif plugin is not None and self.plugins[plugin].has_key(key):
self.plugins[plugin][key] = value
else:
raise KeyError('ConfigBase::set_item: unknown key %s' % key)
return(True)
if __name__ == '__main__':
import doctest
(failed, attempted) = doctest.testmod()
print "%d/%d tests failed" % (failed, attempted)