diff --git a/resources/recipes/mail_and_guardian.recipe b/resources/recipes/mail_and_guardian.recipe new file mode 100644 index 0000000000..5b58f3f938 --- /dev/null +++ b/resources/recipes/mail_and_guardian.recipe @@ -0,0 +1,32 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1295081935(BasicNewsRecipe): + title = u'Mail & Guardian ZA News' + __author__ = '77ja65' + language = 'en' + oldest_article = 7 + max_articles_per_feed = 30 + no_stylesheets = True + masthead_url = 'http://c1608832.cdn.cloudfiles.rackspacecloud.com/mg_logo.gif' + remove_tags_after = [dict(id='content')] + + feeds = [ + (u'National News', u'http://www.mg.co.za/rss/national'), + (u'Top Stories', u'http://www.mg.co.za/rss'), + (u'Africa News', u'http://www.mg.co.za/rss/africa'), + (u'Sport', u'http://www.mg.co.za/rss/sport'), + (u'Business', u'http://www.mg.co.za/rss/business'), + (u'And In Other News', u'http://www.mg.co.za/rss/and-in-other-news'), + (u'World News', u'http://www.mg.co.za/rss/world') + ] + + def print_version(self, url): + return url.replace('http://www.mg.co.za/article/', + 'http://www.mg.co.za/printformat/single/') + + extra_css = ''' + h1{font-family:Arial,Helvetica,sans-serif; font- + weight:bold;font-size:large;} + h2{font-family:Arial,Helvetica,sans-serif; font- + weight:normal;font-size:small;} + ''' diff --git a/resources/template-functions.json b/resources/template-functions.json new file mode 100644 index 0000000000..cb329d771c --- /dev/null +++ b/resources/template-functions.json @@ -0,0 +1,28 @@ +{ + "contains": "def evaluate(self, formatter, kwargs, mi, locals,\n val, test, value_if_present, value_if_not):\n if re.search(test, val):\n return value_if_present\n else:\n return value_if_not\n", + "divide": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x / y)\n", + "uppercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.upper()\n", + "strcat": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n res = ''\n for i in range(0, len(args)):\n res += args[i]\n return res\n", + "substr": "def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_):\n return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)]\n", + "ifempty": "def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):\n if val:\n return val\n else:\n return value_if_empty\n", + "field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return formatter.get_value(name, [], kwargs)\n", + "capitalize": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return capitalize(val)\n", + "list_item": "def evaluate(self, formatter, kwargs, mi, locals, val, index, sep):\n if not val:\n return ''\n index = int(index)\n val = val.split(sep)\n try:\n return val[index]\n except:\n return ''\n", + "shorten": "def evaluate(self, formatter, kwargs, mi, locals,\n val, leading, center_string, trailing):\n l = max(0, int(leading))\n t = max(0, int(trailing))\n if len(val) > l + len(center_string) + t:\n return val[0:l] + center_string + ('' if t == 0 else val[-t:])\n else:\n return val\n", + "re": "def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement):\n return re.sub(pattern, replacement, val)\n", + "add": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x + y)\n", + "lookup": "def evaluate(self, formatter, kwargs, mi, locals, val, *args):\n if len(args) == 2: # here for backwards compatibility\n if val:\n return formatter.vformat('{'+args[0].strip()+'}', [], kwargs)\n else:\n return formatter.vformat('{'+args[1].strip()+'}', [], kwargs)\n if (len(args) % 2) != 1:\n raise ValueError(_('lookup requires either 2 or an odd number of arguments'))\n i = 0\n while i < len(args):\n if i + 1 >= len(args):\n return formatter.vformat('{' + args[i].strip() + '}', [], kwargs)\n if re.search(args[i], val):\n return formatter.vformat('{'+args[i+1].strip() + '}', [], kwargs)\n i += 2\n", + "template": "def evaluate(self, formatter, kwargs, mi, locals, template):\n template = template.replace('[[', '{').replace(']]', '}')\n return formatter.safe_format(template, kwargs, 'TEMPLATE', mi)\n", + "print": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n print args\n return None\n", + "titlecase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return titlecase(val)\n", + "test": "def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set):\n if val:\n return value_if_set\n else:\n return value_not_set\n", + "eval": "def evaluate(self, formatter, kwargs, mi, locals, template):\n from formatter import eval_formatter\n template = template.replace('[[', '{').replace(']]', '}')\n return eval_formatter.safe_format(template, locals, 'EVAL', None)\n", + "multiply": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x * y)\n", + "subtract": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x - y)\n", + "count": "def evaluate(self, formatter, kwargs, mi, locals, val, sep):\n return unicode(len(val.split(sep)))\n", + "lowercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.lower()\n", + "assign": "def evaluate(self, formatter, kwargs, mi, locals, target, value):\n locals[target] = value\n return value\n", + "switch": "def evaluate(self, formatter, kwargs, mi, locals, val, *args):\n if (len(args) % 2) != 1:\n raise ValueError(_('switch requires an odd number of arguments'))\n i = 0\n while i < len(args):\n if i + 1 >= len(args):\n return args[i]\n if re.search(args[i], val):\n return args[i+1]\n i += 2\n", + "strcmp": "def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):\n v = strcmp(x, y)\n if v < 0:\n return lt\n if v == 0:\n return eq\n return gt\n", + "cmp": "def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):\n x = float(x if x else 0)\n y = float(y if y else 0)\n if x < y:\n return lt\n if x == y:\n return eq\n return gt\n" +} \ No newline at end of file diff --git a/setup/resources.py b/setup/resources.py index 977d753828..eedaf0bf6d 100644 --- a/setup/resources.py +++ b/setup/resources.py @@ -84,6 +84,23 @@ class Resources(Command): cPickle.dump(complete, open(dest, 'wb'), -1) + self.info('\tCreating template-functions.json') + dest = self.j(self.RESOURCES, 'template-functions.json') + function_dict = {} + import inspect + from calibre.utils.formatter_functions import all_builtin_functions + for obj in all_builtin_functions: + eval_func = inspect.getmembers(obj, + lambda x: inspect.ismethod(x) and x.__name__ == 'evaluate') + try: + lines = [l[4:] for l in inspect.getsourcelines(eval_func[0][1])[0]] + except: + continue + lines = ''.join(lines) + function_dict[obj.name] = lines + import json + json.dump(function_dict, open(dest, 'wb'), indent=4) + def clean(self): for x in ('scripts', 'recipes', 'ebook-convert-complete'): x = self.j(self.RESOURCES, x+'.pickle') diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index b01ab1ba21..f50251e700 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -94,7 +94,7 @@ class EditMetadataAction(InterfaceAction): get_social_metadata = config['get_social_metadata'] else: get_social_metadata = set_social_metadata - from calibre.gui2.metadata import DoDownload + from calibre.gui2.metadata.bulk_download import DoDownload if set_social_metadata is not None and set_social_metadata: x = _('social metadata') else: diff --git a/src/calibre/gui2/metadata/__init__.py b/src/calibre/gui2/metadata/__init__.py new file mode 100644 index 0000000000..68dfb8d2b5 --- /dev/null +++ b/src/calibre/gui2/metadata/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2011, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + + diff --git a/src/calibre/gui2/metadata.py b/src/calibre/gui2/metadata/bulk_download.py similarity index 100% rename from src/calibre/gui2/metadata.py rename to src/calibre/gui2/metadata/bulk_download.py diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 90d7ce698a..f6eac49426 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -730,7 +730,7 @@ class TagsModel(QAbstractItemModel): # {{{ else: collapse_model = 'partition' collapse_template = tweaks['categories_collapsed_popularity_template'] - collapse_letter = None + collapse_letter = collapse_letter_sk = None for i, r in enumerate(self.row_map): if self.hidden_categories and self.categories[i] in self.hidden_categories: @@ -782,8 +782,17 @@ class TagsModel(QAbstractItemModel): # {{{ ts = tag.sort if not ts: ts = ' ' - if upper(ts[0]) != collapse_letter: + try: + sk = sort_key(ts)[0] + except: + sk = ts[0] + + if sk != collapse_letter_sk: collapse_letter = upper(ts[0]) + try: + collapse_letter_sk = sort_key(collapse_letter)[0] + except: + collapse_letter_sk = collapse_letter sub_cat = TagTreeItem(parent=category, data = collapse_letter, category_icon = category_node.icon, diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index d87bb45f7a..f2ff783a76 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -386,11 +386,13 @@ class LineEditECM(object): action_lower_case = case_menu.addAction(_('Lower Case')) action_swap_case = case_menu.addAction(_('Swap Case')) action_title_case = case_menu.addAction(_('Title Case')) + action_capitalize = case_menu.addAction(_('Capitalize')) self.connect(action_upper_case, SIGNAL('triggered()'), self.upper_case) self.connect(action_lower_case, SIGNAL('triggered()'), self.lower_case) self.connect(action_swap_case, SIGNAL('triggered()'), self.swap_case) self.connect(action_title_case, SIGNAL('triggered()'), self.title_case) + self.connect(action_capitalize, SIGNAL('triggered()'), self.capitalize) menu.addMenu(case_menu) menu.exec_(event.globalPos()) @@ -408,6 +410,10 @@ class LineEditECM(object): from calibre.utils.titlecase import titlecase self.setText(titlecase(unicode(self.text()))) + def capitalize(self): + from calibre.utils.icu import capitalize + self.setText(capitalize(unicode(self.text()))) + class EnLineEdit(LineEditECM, QLineEdit): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index c2381938fb..3a2109e01e 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -694,7 +694,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): aum = [] aus = {} for (author, author_sort) in aut_list: - aum.append(author) + aum.append(author.replace('|', ',')) aus[author] = author_sort.replace('|', ',') mi.title = row[fm['title']] mi.authors = aum diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index a66d787095..8f928cfe87 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -77,7 +77,7 @@ class FormatterFunction(object): exc_traceback)[-2:]).replace('\n', '') return _('Exception ' + info) - +all_builtin_functions = [] class BuiltinFormatterFunction(FormatterFunction): def __init__(self): formatter_functions.register_builtin(self) @@ -88,6 +88,7 @@ class BuiltinFormatterFunction(FormatterFunction): except: lines = [] self.program_text = ''.join(lines) + all_builtin_functions.append(self) class BuiltinStrcmp(BuiltinFormatterFunction): name = 'strcmp' diff --git a/src/calibre/utils/icu.py b/src/calibre/utils/icu.py index 659984e7f9..f17ff1b17f 100644 --- a/src/calibre/utils/icu.py +++ b/src/calibre/utils/icu.py @@ -80,7 +80,7 @@ def icu_case_sensitive_strcmp(collator, a, b): def icu_capitalize(s): s = lower(s) - return s.replace(s[0], upper(s[0]), 1) + return s.replace(s[0], upper(s[0]), 1) if s else s load_icu() load_collator()