diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 15a020e756..2afa65e690 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -27,8 +27,6 @@ def _config(): help=_('Frequently used directories')) c.add_opt('send_to_storage_card_by_default', default=False, help=_('Send file to storage card instead of main memory by default')) - c.add_opt('save_to_disk_single_format', default='lrf', - help=_('The format to use when saving single files to disk')) c.add_opt('confirm_delete', default=False, help=_('Confirm before deleting')) c.add_opt('toolbar_icon_size', default=QSize(48, 48), diff --git a/src/calibre/gui2/dialogs/config.py b/src/calibre/gui2/dialogs/config.py index 1550912e61..8b1e61f546 100644 --- a/src/calibre/gui2/dialogs/config.py +++ b/src/calibre/gui2/dialogs/config.py @@ -391,20 +391,16 @@ class ConfigDialog(QDialog, Ui_Dialog): icons = config['toolbar_icon_size'] self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2) self.show_toolbar_text.setChecked(config['show_text_in_toolbar']) - + output_formats = sorted(available_output_formats()) output_formats.remove('oeb') for f in output_formats: - self.output_format.addItem(f) - default_index = self.output_format.findText(prefs['output_format']) + self.output_format.addItem(f.upper()) + default_index = \ + self.output_format.findText(prefs['output_format'].upper()) self.output_format.setCurrentIndex(default_index if default_index != -1 else 0) - self.book_exts = sorted(BOOK_EXTENSIONS) - for ext in self.book_exts: - self.single_format.addItem(ext.upper(), QVariant(ext)) - single_format = config['save_to_disk_single_format'] - self.single_format.setCurrentIndex(self.book_exts.index(single_format)) self.cover_browse.setValue(config['cover_flow_queue_length']) self.systray_notifications.setChecked(not config['disable_tray_notification']) from calibre.translations.compiled import translations @@ -426,17 +422,17 @@ class ConfigDialog(QDialog, Ui_Dialog): self.pdf_metadata.setChecked(prefs['read_file_metadata']) - added_html = False - for ext in self.book_exts: + exts = set([]) + for ext in BOOK_EXTENSIONS: ext = ext.lower() ext = re.sub(r'(x{0,1})htm(l{0,1})', 'html', ext) if ext == 'lrf' or is_supported('book.'+ext): - if ext == 'html' and added_html: - continue - self.viewer.addItem(ext.upper()) - self.viewer.item(self.viewer.count()-1).setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable) - self.viewer.item(self.viewer.count()-1).setCheckState(Qt.Checked if ext.upper() in config['internally_viewed_formats'] else Qt.Unchecked) - added_html = ext == 'html' + exts.add(ext) + + for ext in sorted(exts): + self.viewer.addItem(ext.upper()) + self.viewer.item(self.viewer.count()-1).setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable) + self.viewer.item(self.viewer.count()-1).setCheckState(Qt.Checked if ext.upper() in config['internally_viewed_formats'] else Qt.Unchecked) self.viewer.sortItems() self.start.setEnabled(not getattr(self.server, 'is_running', False)) self.test.setEnabled(not self.start.isEnabled()) @@ -767,8 +763,7 @@ class ConfigDialog(QDialog, Ui_Dialog): p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()] prefs['worker_process_priority'] = p prefs['read_file_metadata'] = bool(self.pdf_metadata.isChecked()) - prefs['output_format'] = self.output_format.currentText() - config['save_to_disk_single_format'] = self.book_exts[self.single_format.currentIndex()] + prefs['output_format'] = unicode(self.output_format.currentText()).upper() config['cover_flow_queue_length'] = self.cover_browse.value() prefs['language'] = str(self.language.itemData(self.language.currentIndex()).toString()) config['systray_icon'] = self.systray_icon.checkState() == Qt.Checked diff --git a/src/calibre/gui2/dialogs/config.ui b/src/calibre/gui2/dialogs/config.ui index 4d8d64e151..bdf9642d3e 100644 --- a/src/calibre/gui2/dialogs/config.ui +++ b/src/calibre/gui2/dialogs/config.ui @@ -147,19 +147,6 @@ - - - Format for &single file save: - - - single_format - - - - - - - Default network &timeout: @@ -169,7 +156,7 @@ - + Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information) @@ -188,10 +175,10 @@ - + - + Choose &language (requires restart): @@ -201,7 +188,7 @@ - + @@ -220,7 +207,7 @@ - + Job &priority: diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 4a6222dd7e..e59df5abdd 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -201,8 +201,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.save_menu = QMenu() self.save_menu.addAction(_('Save to disk')) self.save_menu.addAction(_('Save to disk in a single directory')) - self.save_menu.addAction(_('Save only %s format to disk')%\ - config.get('save_to_disk_single_format').upper()) self.view_menu = QMenu() self.view_menu.addAction(_('View')) @@ -214,8 +212,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.save_to_disk) QObject.connect(self.save_menu.actions()[1], SIGNAL("triggered(bool)"), self.save_to_single_dir) - QObject.connect(self.save_menu.actions()[2], SIGNAL("triggered(bool)"), - self.save_single_format_to_disk) QObject.connect(self.action_view, SIGNAL("triggered(bool)"), self.view_book) QObject.connect(self.view_menu.actions()[0], diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index 89bc2dfa79..5d0f29b56e 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -42,7 +42,7 @@ 16777215 - 100 + 75 diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index f1f4a6a771..b3e1cd2cf9 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -14,7 +14,7 @@ from optparse import IndentedHelpFormatter from PyQt4.QtCore import QString from calibre.constants import terminal_controller, iswindows, isosx, \ __appname__, __version__, __author__, plugins -from calibre.utils.lock import LockError, ExclusiveFile +from calibre.utils.lock import LockError, ExclusiveFile from collections import defaultdict if os.environ.has_key('CALIBRE_CONFIG_DIRECTORY'): @@ -38,14 +38,14 @@ def make_config_dir(): class CustomHelpFormatter(IndentedHelpFormatter): - + def format_usage(self, usage): return _("%sUsage%s: %s\n") % (terminal_controller.BLUE, terminal_controller.NORMAL, usage) - + def format_heading(self, heading): - return "%*s%s%s%s:\n" % (self.current_indent, terminal_controller.BLUE, + return "%*s%s%s%s:\n" % (self.current_indent, terminal_controller.BLUE, "", heading, terminal_controller.NORMAL) - + def format_option(self, option): result = [] opts = self.option_strings[option] @@ -55,14 +55,14 @@ class CustomHelpFormatter(IndentedHelpFormatter): terminal_controller.GREEN+opts+terminal_controller.NORMAL) indent_first = self.help_position else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width + len(terminal_controller.GREEN + terminal_controller.NORMAL), + opts = "%*s%-*s " % (self.current_indent, "", opt_width + len(terminal_controller.GREEN + terminal_controller.NORMAL), terminal_controller.GREEN + opts + terminal_controller.NORMAL) indent_first = 0 result.append(opts) if option.help: help_text = self.expand_default(option).split('\n') help_lines = [] - + for line in help_text: help_lines.extend(textwrap.wrap(line, self.help_width)) result.append("%*s%s\n" % (indent_first, "", help_lines[0])) @@ -74,7 +74,7 @@ class CustomHelpFormatter(IndentedHelpFormatter): class OptionParser(_OptionParser): - + def __init__(self, usage='%prog [options] filename', version='%%prog (%s %s)'%(__appname__, __version__), @@ -85,16 +85,16 @@ class OptionParser(_OptionParser): usage = textwrap.dedent(usage) usage += '''\n\nWhenever you pass arguments to %prog that have spaces in them, '''\ '''enclose the arguments in quotation marks.''' - _OptionParser.__init__(self, usage=usage, version=version, epilog=epilog, - formatter=CustomHelpFormatter(), + _OptionParser.__init__(self, usage=usage, version=version, epilog=epilog, + formatter=CustomHelpFormatter(), conflict_handler=conflict_handler, **kwds) self.gui_mode = gui_mode - + def error(self, msg): if self.gui_mode: raise Exception(msg) _OptionParser.error(self, msg) - + def merge(self, parser): ''' Add options from parser to self. In case of conflicts, conflicting options from @@ -102,18 +102,18 @@ class OptionParser(_OptionParser): ''' opts = list(parser.option_list) groups = list(parser.option_groups) - + def merge_options(options, container): for opt in deepcopy(options): if not self.has_option(opt.get_opt_string()): container.add_option(opt) - + merge_options(opts, self) - + for group in groups: g = self.add_option_group(group.title) merge_options(group.option_list, g) - + def subsume(self, group_name, msg=''): ''' Move all existing options into a subgroup named @@ -125,7 +125,7 @@ class OptionParser(_OptionParser): for opt in opts: self.remove_option(opt.get_opt_string()) subgroup.add_option(opt) - + def options_iter(self): for opt in self.option_list: if str(opt).strip(): @@ -134,12 +134,12 @@ class OptionParser(_OptionParser): for opt in gr.option_list: if str(opt).strip(): yield opt - + def option_by_dest(self, dest): for opt in self.options_iter(): if opt.dest == dest: return opt - + def merge_options(self, lower, upper): ''' Merge options in lower and upper option lists into upper. @@ -153,16 +153,16 @@ class OptionParser(_OptionParser): if lower.__dict__[dest] != opt.default and \ upper.__dict__[dest] == opt.default: upper.__dict__[dest] = lower.__dict__[dest] - + class Option(object): - - def __init__(self, name, switches=[], help='', type=None, choices=None, + + def __init__(self, name, switches=[], help='', type=None, choices=None, check=None, group=None, default=None, action=None, metavar=None): if choices: type = 'choice' - + self.name = name self.switches = switches self.help = help.replace('%default', repr(default)) if help else None @@ -172,40 +172,40 @@ class Option(object): self.type = 'float' elif isinstance(default, int) and not isinstance(default, bool): self.type = 'int' - + self.choices = choices self.check = check self.group = group self.default = default self.action = action self.metavar = metavar - + def __eq__(self, other): return self.name == getattr(other, 'name', other) - + def __repr__(self): return 'Option: '+self.name - + def __str__(self): return repr(self) - + class OptionValues(object): - + def copy(self): return deepcopy(self) class OptionSet(object): - - OVERRIDE_PAT = re.compile(r'#{3,100} Override Options #{15}(.*?)#{3,100} End Override #{3,100}', + + OVERRIDE_PAT = re.compile(r'#{3,100} Override Options #{15}(.*?)#{3,100} End Override #{3,100}', re.DOTALL|re.IGNORECASE) - + def __init__(self, description=''): self.description = description self.preferences = [] self.group_list = [] self.groups = {} self.set_buffer = {} - + def has_option(self, name_or_option_object): if name_or_option_object in self.preferences: return True @@ -213,14 +213,14 @@ class OptionSet(object): if p.name == name_or_option_object: return True return False - + def add_group(self, name, description=''): if name in self.group_list: raise ValueError('A group by the name %s already exists in this set'%name) self.groups[name] = description self.group_list.append(name) return partial(self.add_opt, group=name) - + def update(self, other): for name in other.groups.keys(): self.groups[name] = other.groups[name] @@ -230,7 +230,7 @@ class OptionSet(object): if pref in self.preferences: self.preferences.remove(pref) self.preferences.append(pref) - + def smart_update(self, opts1, opts2): ''' Updates the preference values in opts1 using only the non-default preference values in opts2. @@ -239,47 +239,47 @@ class OptionSet(object): new = getattr(opts2, pref.name, pref.default) if new != pref.default: setattr(opts1, pref.name, new) - + def remove_opt(self, name): if name in self.preferences: self.preferences.remove(name) - - - def add_opt(self, name, switches=[], help=None, type=None, choices=None, + + + def add_opt(self, name, switches=[], help=None, type=None, choices=None, group=None, default=None, action=None, metavar=None): ''' Add an option to this section. - + :param name: The name of this option. Must be a valid Python identifier. - Must also be unique in this OptionSet and all its subsets. - :param switches: List of command line switches for this option + Must also be unique in this OptionSet and all its subsets. + :param switches: List of command line switches for this option (as supplied to :module:`optparse`). If empty, this option will not be added to the command line parser. :param help: Help text. :param type: Type checking of option values. Supported types are: `None, 'choice', 'complex', 'float', 'int', 'string'`. :param choices: List of strings or `None`. - :param group: Group this option belongs to. You must previously + :param group: Group this option belongs to. You must previously have created this group with a call to :method:`add_group`. :param default: The default value for this option. :param action: The action to pass to optparse. Supported values are: `None, 'count'`. For choices and boolean options, action is automatically set correctly. ''' - pref = Option(name, switches=switches, help=help, type=type, choices=choices, + pref = Option(name, switches=switches, help=help, type=type, choices=choices, group=group, default=default, action=action, metavar=None) if group is not None and group not in self.groups.keys(): raise ValueError('Group %s has not been added to this section'%group) if pref in self.preferences: raise ValueError('An option with the name %s already exists in this set.'%name) self.preferences.append(pref) - + def option_parser(self, user_defaults=None, usage='', gui_mode=False): parser = OptionParser(usage, gui_mode=gui_mode) groups = defaultdict(lambda : parser) for group, desc in self.groups.items(): groups[group] = parser.add_option_group(group.upper(), desc) - + for pref in self.preferences: if not pref.switches: continue @@ -299,16 +299,16 @@ class OptionSet(object): action=action, ) g.add_option(*pref.switches, **args) - - + + return parser - + def get_override_section(self, src): match = self.OVERRIDE_PAT.search(src) if match: return match.group() return '' - + def parse_string(self, src): options = {'cPickle':cPickle} if not isinstance(src, unicode): @@ -327,9 +327,9 @@ class OptionSet(object): if callable(formatter): val = formatter(val) setattr(opts, pref.name, val) - + return opts - + def render_group(self, name, desc, opts): prefs = [pref for pref in self.preferences if pref.group == name] lines = ['### Begin group: %s'%(name if name else 'DEFAULT')] @@ -340,11 +340,11 @@ class OptionSet(object): lines.append('# '+pref.name.replace('_', ' ')) if pref.help: lines += map(lambda x: '# ' + x, pref.help.split('\n')) - lines.append('%s = %s'%(pref.name, + lines.append('%s = %s'%(pref.name, self.serialize_opt(getattr(opts, pref.name, pref.default)))) lines.append(' ') return '\n'.join(lines) - + def serialize_opt(self, val): if val is val is True or val is False or val is None or \ isinstance(val, (int, float, long, basestring)): @@ -353,7 +353,7 @@ class OptionSet(object): return repr(unicode(val)) pickle = cPickle.dumps(val, -1) return 'cPickle.loads(%s)'%repr(pickle) - + def serialize(self, opts): src = '# %s\n\n'%(self.description.replace('\n', '\n# ')) groups = [self.render_group(name, self.groups.get(name, ''), opts) \ @@ -361,34 +361,34 @@ class OptionSet(object): return src + '\n\n'.join(groups) class ConfigInterface(object): - + def __init__(self, description): self.option_set = OptionSet(description=description) self.add_opt = self.option_set.add_opt self.add_group = self.option_set.add_group self.remove_opt = self.remove = self.option_set.remove_opt self.parse_string = self.option_set.parse_string - + def update(self, other): self.option_set.update(other.option_set) - + def option_parser(self, usage='', gui_mode=False): - return self.option_set.option_parser(user_defaults=self.parse(), + return self.option_set.option_parser(user_defaults=self.parse(), usage=usage, gui_mode=gui_mode) - + def smart_update(self, opts1, opts2): self.option_set.smart_update(opts1, opts2) - + class Config(ConfigInterface): ''' A file based configuration. ''' - + def __init__(self, basename, description=''): ConfigInterface.__init__(self, description) self.config_file_path = os.path.join(config_dir, basename+'.py') - - + + def parse(self): src = '' if os.path.exists(self.config_file_path): @@ -398,7 +398,7 @@ class Config(ConfigInterface): except LockError: raise IOError('Could not lock config file: %s'%self.config_file_path) return self.option_set.parse_string(src) - + def as_string(self): if not os.path.exists(self.config_file_path): return '' @@ -407,7 +407,7 @@ class Config(ConfigInterface): return f.read().decode('utf-8') except LockError: raise IOError('Could not lock config file: %s'%self.config_file_path) - + def set(self, name, val): if not self.option_set.has_option(name): raise ValueError('The option %s is not defined.'%name) @@ -427,19 +427,19 @@ class Config(ConfigInterface): f.write(src) except LockError: raise IOError('Could not lock config file: %s'%self.config_file_path) - + class StringConfig(ConfigInterface): ''' A string based configuration ''' - + def __init__(self, src, description=''): ConfigInterface.__init__(self, description) self.src = src - + def parse(self): return self.option_set.parse_string(self.src) - + def set(self, name, val): if not self.option_set.has_option(name): raise ValueError('The option %s is not defined.'%name) @@ -452,30 +452,30 @@ class ConfigProxy(object): ''' A Proxy to minimize file reads for widely used config settings ''' - + def __init__(self, config): self.__config = config - self.__opts = None - + self.__opts = None + def refresh(self): self.__opts = self.__config.parse() - + def __getitem__(self, key): return self.get(key) - + def __setitem__(self, key, val): return self.set(key, val) - + def get(self, key): if self.__opts is None: self.refresh() return getattr(self.__opts, key) - + def set(self, key, val): if self.__opts is None: self.refresh() setattr(self.__opts, key, val) - return self.__config.set(key, val) + return self.__config.set(key, val) class DynamicConfig(dict): ''' @@ -489,7 +489,7 @@ class DynamicConfig(dict): self.name = name self.file_path = os.path.join(config_dir, name+'.pickle') self.refresh() - + def refresh(self): d = {} if os.path.exists(self.file_path): @@ -503,20 +503,20 @@ class DynamicConfig(dict): d = {} self.clear() self.update(d) - + def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: return None - + def __setitem__(self, key, val): dict.__setitem__(self, key, val) self.commit() - + def set(self, key, val): self.__setitem__(key, val) - + def commit(self): if hasattr(self, 'file_path') and self.file_path: if not os.path.exists(self.file_path): @@ -526,17 +526,17 @@ class DynamicConfig(dict): f.seek(0) f.truncate() f.write(raw) - -dynamic = DynamicConfig() + +dynamic = DynamicConfig() def _prefs(): c = Config('global', 'calibre wide preferences') - c.add_opt('database_path', + c.add_opt('database_path', default=os.path.expanduser('~/library1.db'), help=_('Path to the database in which books are stored')) c.add_opt('filename_pattern', default=ur'(?P.+) - (?P<author>[^_]+)', help=_('Pattern to guess metadata from filenames')) - c.add_opt('isbndb_com_key', default='', + c.add_opt('isbndb_com_key', default='', help=_('Access key for isbndb.com')) c.add_opt('network_timeout', default=5, help=_('Default timeout for network operations (seconds)')) @@ -544,13 +544,13 @@ def _prefs(): help=_('Path to directory in which your library of books is stored')) c.add_opt('language', default=None, help=_('The language in which to display the user interface')) - c.add_opt('output_format', default='EPUB', + c.add_opt('output_format', default='EPUB', help=_('The default output format for ebook conversions.')) c.add_opt('read_file_metadata', default=True, help=_('Read metadata from files')) - c.add_opt('worker_process_priority', default='normal', + c.add_opt('worker_process_priority', default='normal', help=_('The priority of worker processes')) - + c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.') return c @@ -565,11 +565,11 @@ def migrate(): from PyQt4.QtCore import QSettings, QVariant class Settings(QSettings): - + def __init__(self, name='calibre2'): QSettings.__init__(self, QSettings.IniFormat, QSettings.UserScope, 'kovidgoyal.net', name) - + def get(self, key, default=None): try: key = str(key) @@ -581,7 +581,7 @@ def migrate(): return cPickle.loads(val) except: return default - + s, migrated = Settings(), set([]) all_keys = set(map(unicode, s.allKeys())) from calibre.gui2 import config, dynamic @@ -599,13 +599,13 @@ def migrate(): pass finally: migrated.add(key) - - + + _migrate('database path', p=prefs) _migrate('filename pattern', p=prefs) _migrate('network timeout', p=prefs) _migrate('isbndb.com key', p=prefs) - + _migrate('frequently used directories') _migrate('send to device by default') _migrate('save to disk single format') @@ -616,33 +616,33 @@ def migrate(): _migrate('cover flow queue length') _migrate('LRF conversion defaults') _migrate('LRF ebook viewer options') - + for key in all_keys - migrated: if key.endswith(': un') or key.endswith(': pw'): _migrate(key, p=dynamic) p.set('migrated', True) - - + + if __name__ == '__main__': import subprocess from PyQt4.Qt import QByteArray c = Config('test', 'test config') - + c.add_opt('one', ['-1', '--one'], help="This is option #1") c.set('one', u'345') - + c.add_opt('two', help="This is option #2") c.set('two', 345) - + c.add_opt('three', help="This is option #3") c.set('three', QString(u'aflatoon')) - + c.add_opt('four', help="This is option #4") c.set('four', QByteArray('binary aflatoon')) - + subprocess.call(['pygmentize', os.path.expanduser('~/.config/calibre/test.py')]) - + opts = c.parse() for i in ('one', 'two', 'three', 'four'): print i, repr(getattr(opts, i)) - +