mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
KG updates
This commit is contained in:
commit
e2f8169871
43
recipes/alt_om_herning.recipe
Normal file
43
recipes/alt_om_herning.recipe
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Rasmus Lauritsen <rasmus at lauritsen.info>'
|
||||||
|
'''
|
||||||
|
aoh.dk
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class aoh_dk(BasicNewsRecipe):
|
||||||
|
title = 'Alt om Herning'
|
||||||
|
__author__ = 'Rasmus Lauritsen'
|
||||||
|
description = 'Nyheder fra Herning om omegn'
|
||||||
|
publisher = 'Mediehuset Herning Folkeblad'
|
||||||
|
category = 'news, local, Denmark'
|
||||||
|
oldest_article = 14
|
||||||
|
max_articles_per_feed = 50
|
||||||
|
no_stylesheets = True
|
||||||
|
delay = 1
|
||||||
|
encoding = 'utf8'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'da'
|
||||||
|
extra_css = """ body{font-family: Verdana,Arial,sans-serif }
|
||||||
|
img{margin-bottom: 0.4em}
|
||||||
|
.txtContent,.stamp{font-size: small}
|
||||||
|
"""
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
feeds = [(u'All news', u'http://aoh.dk/rss.xml')]
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='h1')
|
||||||
|
,dict(name='span', attrs={'class':['frontpage_body']})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name=['object','link'])
|
||||||
|
]
|
@ -71,7 +71,7 @@ class Mediapart(BasicNewsRecipe):
|
|||||||
br = BasicNewsRecipe.get_browser()
|
br = BasicNewsRecipe.get_browser()
|
||||||
if self.username is not None and self.password is not None:
|
if self.username is not None and self.password is not None:
|
||||||
br.open('http://www.mediapart.fr/')
|
br.open('http://www.mediapart.fr/')
|
||||||
br.select_form(nr=1)
|
br.select_form(nr=0)
|
||||||
br['name'] = self.username
|
br['name'] = self.username
|
||||||
br['pass'] = self.password
|
br['pass'] = self.password
|
||||||
br.submit()
|
br.submit()
|
||||||
|
29
recipes/metro_uk.recipe
Normal file
29
recipes/metro_uk.recipe
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||||
|
title = u'Metro UK'
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
oldest_article = 1
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
|
||||||
|
__author__ = 'Dave Asbury'
|
||||||
|
language = 'en_GB'
|
||||||
|
simultaneous_downloads= 3
|
||||||
|
|
||||||
|
masthead_url = 'http://e-edition.metro.co.uk/images/metro_logo.gif'
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(attrs={'class':['img-cnt figure']}),
|
||||||
|
dict(attrs={'class':['art-img']}),
|
||||||
|
dict(name='h1'),
|
||||||
|
dict(name='h2', attrs={'class':'h2'}),
|
||||||
|
dict(name='div', attrs={'class':'art-lft'})
|
||||||
|
]
|
||||||
|
remove_tags = [dict(name='div', attrs={'class':[ 'metroCommentFormWrap',
|
||||||
|
'commentForm', 'metroCommentInnerWrap',
|
||||||
|
'art-rgt','pluck-app pluck-comm','news m12 clrd clr-l p5t', 'flt-r' ]})]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'News', u'http://www.metro.co.uk/rss/news/'), (u'Money', u'http://www.metro.co.uk/rss/money/'), (u'Sport', u'http://www.metro.co.uk/rss/sport/'), (u'Film', u'http://www.metro.co.uk/rss/metrolife/film/'), (u'Music', u'http://www.metro.co.uk/rss/metrolife/music/'), (u'TV', u'http://www.metro.co.uk/rss/tv/'), (u'Showbiz', u'http://www.metro.co.uk/rss/showbiz/'), (u'Weird News', u'http://www.metro.co.uk/rss/weird/'), (u'Travel', u'http://www.metro.co.uk/rss/travel/'), (u'Lifestyle', u'http://www.metro.co.uk/rss/lifestyle/'), (u'Books', u'http://www.metro.co.uk/rss/lifestyle/books/'), (u'Food', u'http://www.metro.co.uk/rss/lifestyle/restaurants/')]
|
||||||
|
|
||||||
|
|
64
recipes/version2.recipe
Normal file
64
recipes/version2.recipe
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Rasmus Lauritsen <rasmus at lauritsen.info>'
|
||||||
|
'''
|
||||||
|
version2.dk
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class version2(BasicNewsRecipe):
|
||||||
|
title = 'Version2.dk'
|
||||||
|
__author__ = 'Rasmus Lauritsen'
|
||||||
|
description = 'IT News'
|
||||||
|
publisher = 'version2.dk'
|
||||||
|
category = 'news, IT, hardware, software, Denmark'
|
||||||
|
oldest_article = 14
|
||||||
|
max_articles_per_feed = 50
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_empty_feeds = True
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'iso-8859-1'
|
||||||
|
language = 'da'
|
||||||
|
|
||||||
|
extra_css = """
|
||||||
|
body {font-family: "Verdana",Times,serif}
|
||||||
|
.articleauthor{color: #9F9F9F;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: small;
|
||||||
|
text-transform: uppercase}
|
||||||
|
.rubric,.dd,h6#credit{color: #CD0021;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: small;
|
||||||
|
text-transform: uppercase}
|
||||||
|
.descender:first-letter{display: inline; font-size: xx-large; font-weight: bold}
|
||||||
|
.dd,h6#credit{color: gray}
|
||||||
|
.c{display: block}
|
||||||
|
.caption,h2#articleintro{font-style: italic}
|
||||||
|
.caption{font-size: small}
|
||||||
|
"""
|
||||||
|
|
||||||
|
preprocess_regexps = [ (re.compile(r'</?a[^>]*>'),lambda match: ''),
|
||||||
|
(re.compile(r'<span[^>]*article-link-id.*?<br\s*\/?><br\s*\/?>'), lambda match: '')]
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'class':'article'})]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='p',attrs={'class':'meta links'}),
|
||||||
|
dict(name='div',attrs={'class':'float-right'}),
|
||||||
|
dict(name='span',attrs={'class':'article-link-id'})
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Seneste nyheder' , u'http://www.version2.dk/feeds/nyheder')
|
||||||
|
,(u'Forretningssoftware' , u'http://www.version2.dk/feeds/forretningssoftware')
|
||||||
|
,(u'Internet & styresystemer' , u'http://www.version2.dk/feeds/styresystemer')
|
||||||
|
,(u'It-arkitektur' , u'http://www.version2.dk/feeds/it-arkitektur')
|
||||||
|
,(u'It-styring & outsourcing' , u'http://www.version2.dk/feeds/it-styring')
|
||||||
|
,(u'Job & karriere' , u'http://www.version2.dk/feeds/karriere')
|
||||||
|
,(u'Mobil it & tele' , u'http://www.version2.dk/feeds/tele')
|
||||||
|
,(u'Server/storage & netværk' , u'http://www.version2.dk/feeds/server-storage')
|
||||||
|
,(u'Sikkerhed' , u'http://www.version2.dk/feeds/sikkerhed')
|
||||||
|
,(u'Softwareudvikling' , u'http://www.version2.dk/feeds/softwareudvikling')
|
||||||
|
]
|
@ -1,26 +1,27 @@
|
|||||||
{
|
{
|
||||||
"and": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if not args[i]:\n return ''\n i += 1\n return '1'\n",
|
"and": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if not args[i]:\n return ''\n i += 1\n return '1'\n",
|
||||||
"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",
|
"contains": "def evaluate(self, formatter, kwargs, mi, locals,\n val, test, value_if_present, value_if_not):\n if re.search(test, val, flags=re.I):\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",
|
"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",
|
"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",
|
"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",
|
||||||
"in_list": "def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):\n l = [v.strip() for v in val.split(sep) if v.strip()]\n for v in l:\n if re.search(pat, v):\n return fv\n return nfv\n",
|
"in_list": "def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):\n l = [v.strip() for v in val.split(sep) if v.strip()]\n if l:\n for v in l:\n if re.search(pat, v, flags=re.I):\n return fv\n return nfv\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",
|
"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",
|
||||||
"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",
|
"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",
|
||||||
"booksize": "def evaluate(self, formatter, kwargs, mi, locals):\n if mi.book_size is not None:\n try:\n return str(mi.book_size)\n except:\n pass\n return ''\n",
|
"booksize": "def evaluate(self, formatter, kwargs, mi, locals):\n if mi.book_size is not None:\n try:\n return str(mi.book_size)\n except:\n pass\n return ''\n",
|
||||||
"select": "def evaluate(self, formatter, kwargs, mi, locals, val, key):\n if not val:\n return ''\n vals = [v.strip() for v in val.split(',')]\n for v in vals:\n if v.startswith(key+':'):\n return v[len(key)+1:]\n return ''\n",
|
"select": "def evaluate(self, formatter, kwargs, mi, locals, val, key):\n if not val:\n return ''\n vals = [v.strip() for v in val.split(',')]\n for v in vals:\n if v.startswith(key+':'):\n return v[len(key)+1:]\n return ''\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",
|
"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",
|
||||||
"first_non_empty": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if args[i]:\n return args[i]\n i += 1\n return ''\n",
|
"first_non_empty": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if args[i]:\n return args[i]\n i += 1\n return ''\n",
|
||||||
"re": "def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement):\n return re.sub(pattern, replacement, val)\n",
|
"re": "def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement):\n return re.sub(pattern, replacement, val, flags=re.I)\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",
|
"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",
|
||||||
"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",
|
"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",
|
"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",
|
||||||
"field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return formatter.get_value(name, [], kwargs)\n",
|
"field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return formatter.get_value(name, [], kwargs)\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",
|
"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",
|
"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, flags=re.I):\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.__class__().safe_format(template, kwargs, 'TEMPLATE', mi)\n",
|
"template": "def evaluate(self, formatter, kwargs, mi, locals, template):\n template = template.replace('[[', '{').replace(']]', '}')\n return formatter.__class__().safe_format(template, kwargs, 'TEMPLATE', mi)\n",
|
||||||
"print": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n print args\n return None\n",
|
"print": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n print args\n return None\n",
|
||||||
"merge_lists": "def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator):\n l1 = [l.strip() for l in list1.split(separator) if l.strip()]\n l2 = [l.strip() for l in list2.split(separator) if l.strip()]\n lcl1 = set([icu_lower(l) for l in l1])\n res = []\n for i in l1:\n res.append(i)\n for i in l2:\n if icu_lower(i) not in lcl1:\n res.append(i)\n return ', '.join(sorted(res, key=sort_key))\n",
|
"merge_lists": "def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator):\n l1 = [l.strip() for l in list1.split(separator) if l.strip()]\n l2 = [l.strip() for l in list2.split(separator) if l.strip()]\n lcl1 = set([icu_lower(l) for l in l1])\n res = []\n for i in l1:\n res.append(i)\n for i in l2:\n if icu_lower(i) not in lcl1:\n res.append(i)\n return ', '.join(sorted(res, key=sort_key))\n",
|
||||||
|
"str_in_list": "def evaluate(self, formatter, kwargs, mi, locals, val, sep, str, fv, nfv):\n l = [v.strip() for v in val.split(sep) if v.strip()]\n c = [v.strip() for v in str.split(sep) if v.strip()]\n if l:\n for v in l:\n for t in c:\n if strcmp(t, v) == 0:\n return fv\n return nfv\n",
|
||||||
"titlecase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return titlecase(val)\n",
|
"titlecase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return titlecase(val)\n",
|
||||||
"subitems": "def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index):\n if not val:\n return ''\n si = int(start_index)\n ei = int(end_index)\n items = [v.strip() for v in val.split(',')]\n rv = set()\n for item in items:\n component = item.split('.')\n try:\n if ei == 0:\n rv.add('.'.join(component[si:]))\n else:\n rv.add('.'.join(component[si:ei]))\n except:\n pass\n return ', '.join(sorted(rv, key=sort_key))\n",
|
"subitems": "def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index):\n if not val:\n return ''\n si = int(start_index)\n ei = int(end_index)\n items = [v.strip() for v in val.split(',')]\n rv = set()\n for item in items:\n component = item.split('.')\n try:\n if ei == 0:\n rv.add('.'.join(component[si:]))\n else:\n rv.add('.'.join(component[si:ei]))\n except:\n pass\n return ', '.join(sorted(rv, key=sort_key))\n",
|
||||||
"sublist": "def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep):\n if not val:\n return ''\n si = int(start_index)\n ei = int(end_index)\n val = val.split(sep)\n try:\n if ei == 0:\n return sep.join(val[si:])\n else:\n return sep.join(val[si:ei])\n except:\n return ''\n",
|
"sublist": "def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep):\n if not val:\n return ''\n si = int(start_index)\n ei = int(end_index)\n val = val.split(sep)\n try:\n if ei == 0:\n return sep.join(val[si:])\n else:\n return sep.join(val[si:ei])\n except:\n return ''\n",
|
||||||
@ -32,9 +33,10 @@
|
|||||||
"count": "def evaluate(self, formatter, kwargs, mi, locals, val, sep):\n return unicode(len(val.split(sep)))\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",
|
"lowercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.lower()\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",
|
"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",
|
||||||
"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",
|
|
||||||
"or": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if args[i]:\n return '1'\n i += 1\n return ''\n",
|
"or": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if args[i]:\n return '1'\n i += 1\n return ''\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, flags=re.I):\n return args[i+1]\n i += 2\n",
|
||||||
|
"ondevice": "def evaluate(self, formatter, kwargs, mi, locals):\n if mi.ondevice_col:\n return _('Yes')\n return ''\n",
|
||||||
|
"assign": "def evaluate(self, formatter, kwargs, mi, locals, target, value):\n locals[target] = value\n return value\n",
|
||||||
"raw_field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return unicode(getattr(mi, name, None))\n",
|
"raw_field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return unicode(getattr(mi, name, None))\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"
|
"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"
|
||||||
}
|
}
|
@ -74,7 +74,7 @@ class Metadata(object):
|
|||||||
Metadata from custom columns should be accessed via the get() method,
|
Metadata from custom columns should be accessed via the get() method,
|
||||||
passing in the lookup name for the column, for example: "#mytags".
|
passing in the lookup name for the column, for example: "#mytags".
|
||||||
|
|
||||||
Use the :meth:`is_null` method to test if a filed is null.
|
Use the :meth:`is_null` method to test if a field is null.
|
||||||
|
|
||||||
This object also has functions to format fields into strings.
|
This object also has functions to format fields into strings.
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ class Metadata(object):
|
|||||||
|
|
||||||
def is_null(self, field):
|
def is_null(self, field):
|
||||||
'''
|
'''
|
||||||
Return True if the value of filed is null in this object.
|
Return True if the value of field is null in this object.
|
||||||
'null' means it is unknown or evaluates to False. So a title of
|
'null' means it is unknown or evaluates to False. So a title of
|
||||||
_('Unknown') is null or a language of 'und' is null.
|
_('Unknown') is null or a language of 'und' is null.
|
||||||
|
|
||||||
|
@ -152,7 +152,8 @@ class DeleteAction(InterfaceAction):
|
|||||||
if not ids:
|
if not ids:
|
||||||
return
|
return
|
||||||
fmts = self._get_selected_formats(
|
fmts = self._get_selected_formats(
|
||||||
'<p>'+_('Choose formats <b>not</b> to be deleted'), ids)
|
'<p>'+_('Choose formats <b>not</b> to be deleted.<p>Note that '
|
||||||
|
'this will never remove all formats from a book.'), ids)
|
||||||
if fmts is None:
|
if fmts is None:
|
||||||
return
|
return
|
||||||
for id in ids:
|
for id in ids:
|
||||||
@ -161,9 +162,12 @@ class DeleteAction(InterfaceAction):
|
|||||||
continue
|
continue
|
||||||
bfmts = set([x.lower() for x in bfmts.split(',')])
|
bfmts = set([x.lower() for x in bfmts.split(',')])
|
||||||
rfmts = bfmts - set(fmts)
|
rfmts = bfmts - set(fmts)
|
||||||
for fmt in rfmts:
|
if bfmts - rfmts:
|
||||||
self.gui.library_view.model().db.remove_format(id, fmt,
|
# Do not delete if it will leave the book with no
|
||||||
index_is_id=True, notify=False)
|
# formats
|
||||||
|
for fmt in rfmts:
|
||||||
|
self.gui.library_view.model().db.remove_format(id, fmt,
|
||||||
|
index_is_id=True, notify=False)
|
||||||
self.gui.library_view.model().refresh_ids(ids)
|
self.gui.library_view.model().refresh_ids(ids)
|
||||||
self.gui.library_view.model().current_changed(self.gui.library_view.currentIndex(),
|
self.gui.library_view.model().current_changed(self.gui.library_view.currentIndex(),
|
||||||
self.gui.library_view.currentIndex())
|
self.gui.library_view.currentIndex())
|
||||||
|
@ -44,7 +44,7 @@ class SelectFormats(QDialog):
|
|||||||
self.setLayout(self._l)
|
self.setLayout(self._l)
|
||||||
self.setWindowTitle(_('Choose formats'))
|
self.setWindowTitle(_('Choose formats'))
|
||||||
self._m = QLabel(msg)
|
self._m = QLabel(msg)
|
||||||
self._m.setWordWrap = True
|
self._m.setWordWrap(True)
|
||||||
self._l.addWidget(self._m)
|
self._l.addWidget(self._m)
|
||||||
self.formats = Formats(fmt_list)
|
self.formats = Formats(fmt_list)
|
||||||
self.fview = QListView(self)
|
self.fview = QListView(self)
|
||||||
|
@ -5,12 +5,17 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel, QCheckBox,
|
from functools import partial
|
||||||
QDialogButtonBox, QColor, QComboBox, QIcon)
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from PyQt4.Qt import (Qt, QLineEdit, QDialog, QGridLayout, QLabel, QCheckBox,
|
||||||
|
QIcon, QDialogButtonBox, QColor, QComboBox, QPushButton)
|
||||||
|
|
||||||
|
from calibre.ebooks.metadata.book.base import composite_formatter
|
||||||
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||||
from calibre.gui2.complete import MultiCompleteLineEdit
|
from calibre.gui2.complete import MultiCompleteLineEdit
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
|
from calibre.utils.icu import sort_key
|
||||||
|
|
||||||
class TemplateLineEditor(QLineEdit):
|
class TemplateLineEditor(QLineEdit):
|
||||||
|
|
||||||
@ -22,114 +27,235 @@ class TemplateLineEditor(QLineEdit):
|
|||||||
QLineEdit.__init__(self, parent)
|
QLineEdit.__init__(self, parent)
|
||||||
self.tags = None
|
self.tags = None
|
||||||
self.mi = None
|
self.mi = None
|
||||||
|
self.txt = None
|
||||||
|
|
||||||
def set_mi(self, mi):
|
def set_mi(self, mi):
|
||||||
self.mi = mi
|
self.mi = mi
|
||||||
|
|
||||||
def set_tags(self, tags):
|
def set_db(self, db):
|
||||||
self.tags = tags
|
self.db = db
|
||||||
|
|
||||||
def contextMenuEvent(self, event):
|
def contextMenuEvent(self, event):
|
||||||
menu = self.createStandardContextMenu()
|
menu = self.createStandardContextMenu()
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
|
|
||||||
|
action_clear_field = menu.addAction(_('Remove any template from the box'))
|
||||||
|
action_clear_field.triggered.connect(self.clear_field)
|
||||||
action_open_editor = menu.addAction(_('Open Template Editor'))
|
action_open_editor = menu.addAction(_('Open Template Editor'))
|
||||||
action_open_editor.triggered.connect(self.open_editor)
|
action_open_editor.triggered.connect(self.open_editor)
|
||||||
if self.tags:
|
|
||||||
action_tag_wizard = menu.addAction(_('Open Tag Wizard'))
|
|
||||||
action_tag_wizard.triggered.connect(self.tag_wizard)
|
|
||||||
menu.exec_(event.globalPos())
|
menu.exec_(event.globalPos())
|
||||||
|
|
||||||
|
def clear_field(self):
|
||||||
|
self.setText('')
|
||||||
|
self.txt = None
|
||||||
|
self.setReadOnly(False)
|
||||||
|
self.setStyleSheet('TemplateLineEditor { color: black }')
|
||||||
|
|
||||||
def open_editor(self):
|
def open_editor(self):
|
||||||
t = TemplateDialog(self, self.text(), self.mi)
|
if self.txt:
|
||||||
|
t = TemplateDialog(self, self.txt, self.mi)
|
||||||
|
else:
|
||||||
|
t = TemplateDialog(self, self.text(), self.mi)
|
||||||
t.setWindowTitle(_('Edit template'))
|
t.setWindowTitle(_('Edit template'))
|
||||||
if t.exec_():
|
if t.exec_():
|
||||||
|
self.txt = None
|
||||||
self.setText(t.textbox.toPlainText())
|
self.setText(t.textbox.toPlainText())
|
||||||
|
|
||||||
|
def enable_wizard_button(self, txt):
|
||||||
|
if not txt or txt.startswith('program:\n#tag wizard'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def setText(self, txt):
|
||||||
|
txt = unicode(txt)
|
||||||
|
if txt and txt.startswith('program:\n#tag wizard'):
|
||||||
|
self.txt = txt
|
||||||
|
self.setReadOnly(True)
|
||||||
|
QLineEdit.setText(self, '')
|
||||||
|
QLineEdit.setText(self, _('Template generated by the wizard'))
|
||||||
|
self.setStyleSheet('TemplateLineEditor { color: gray }')
|
||||||
|
else:
|
||||||
|
QLineEdit.setText(self, txt)
|
||||||
|
|
||||||
def tag_wizard(self):
|
def tag_wizard(self):
|
||||||
txt = unicode(self.text())
|
txt = unicode(self.text())
|
||||||
if txt and not txt.startswith('program:\n#tag wizard'):
|
if txt and not self.txt:
|
||||||
error_dialog(self, _('Invalid text'),
|
error_dialog(self, _('Invalid text'),
|
||||||
_('The text in the box was not generated by this wizard'),
|
_('The text in the box was not generated by this wizard'),
|
||||||
show=True, show_copy_button=False)
|
show=True, show_copy_button=False)
|
||||||
return
|
return
|
||||||
d = TagWizard(self, self.tags, unicode(self.text()))
|
d = TagWizard(self, self.db, unicode(self.txt), self.mi)
|
||||||
if d.exec_():
|
if d.exec_():
|
||||||
self.setText(d.template)
|
self.setText(d.template)
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
if self.txt:
|
||||||
|
return self.txt
|
||||||
|
return QLineEdit.text(self)
|
||||||
|
|
||||||
class TagWizard(QDialog):
|
class TagWizard(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent, tags, txt):
|
def __init__(self, parent, db, txt, mi):
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
self.setWindowTitle(_('Tag Wizard'))
|
self.setWindowTitle(_('Coloring Wizard'))
|
||||||
self.setWindowIcon(QIcon(I('wizard.png')))
|
self.setWindowIcon(QIcon(I('wizard.png')))
|
||||||
|
|
||||||
self.tags = tags
|
self.mi = mi
|
||||||
|
|
||||||
|
self.columns = []
|
||||||
|
self.completion_values = defaultdict(dict)
|
||||||
|
for k in db.all_field_keys():
|
||||||
|
m = db.metadata_for_field(k)
|
||||||
|
if m['datatype'] in ('text', 'enumeration', 'series') and \
|
||||||
|
m['is_category'] and k not in ('identifiers'):
|
||||||
|
self.columns.append(k)
|
||||||
|
if m['is_custom']:
|
||||||
|
self.completion_values[k]['v'] = db.all_custom(m['label'])
|
||||||
|
elif k == 'tags':
|
||||||
|
self.completion_values[k]['v'] = db.all_tags()
|
||||||
|
elif k == 'formats':
|
||||||
|
self.completion_values[k]['v'] = db.all_formats()
|
||||||
|
else:
|
||||||
|
if k in ('publisher'):
|
||||||
|
ck = k + 's'
|
||||||
|
else:
|
||||||
|
ck = k
|
||||||
|
f = getattr(db, 'all_' + ck, None)
|
||||||
|
if f:
|
||||||
|
if k == 'authors':
|
||||||
|
self.completion_values[k]['v'] = [v[1].\
|
||||||
|
replace('|', ',') for v in f()]
|
||||||
|
else:
|
||||||
|
self.completion_values[k]['v'] = [v[1] for v in f()]
|
||||||
|
|
||||||
|
if k in self.completion_values:
|
||||||
|
if k == 'authors':
|
||||||
|
self.completion_values[k]['m'] = None
|
||||||
|
else:
|
||||||
|
self.completion_values[k]['m'] = m['is_multiple']
|
||||||
|
|
||||||
|
self.columns.sort(key=sort_key)
|
||||||
|
self.columns.insert(0, '')
|
||||||
|
|
||||||
l = QGridLayout()
|
l = QGridLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
l.setColumnStretch(0, 1)
|
l.setColumnStretch(2, 10)
|
||||||
l.setColumnMinimumWidth(0, 300)
|
l.setColumnMinimumWidth(3, 300)
|
||||||
h = QLabel(_('Tags (see the popup help for more information)'))
|
|
||||||
|
h = QLabel(_('And'))
|
||||||
h.setToolTip('<p>' +
|
h.setToolTip('<p>' +
|
||||||
_('You can enter more than one tag per box, separated by commas. '
|
_('Set this box to indicate that the two conditions must both '
|
||||||
'The comparison ignores letter case.<br>'
|
'be true to return the "color if value found". For example, you '
|
||||||
'A tag value can be a regular expression. Check the box to turn '
|
'can check if two tags are present, if the book has a tag '
|
||||||
|
'and a #read custom column is checked, or if a book has '
|
||||||
|
'some tag and has a particular format.'))
|
||||||
|
l.addWidget(h, 0, 0, 1, 1)
|
||||||
|
|
||||||
|
h = QLabel(_('Column'))
|
||||||
|
h.setAlignment(Qt.AlignCenter)
|
||||||
|
l.addWidget(h, 0, 1, 1, 1)
|
||||||
|
|
||||||
|
h = QLabel(_('Not'))
|
||||||
|
h.setToolTip('<p>' +
|
||||||
|
_('Set this box to indicate that the value must <b>not</b> match '
|
||||||
|
'to return the "color if value found". For example, you '
|
||||||
|
'can check if a tag does not exist by entering that tag '
|
||||||
|
'and checking this box. You can check if tags are empty by '
|
||||||
|
'checking this box, entering .* (period asterisk) for the text, '
|
||||||
|
'then checking the RE box. The .* regular expression matches '
|
||||||
|
'anything, so if this box is checked, it matches nothing. '
|
||||||
|
'This box is particularly useful when using the AND box.'))
|
||||||
|
h.setAlignment(Qt.AlignCenter)
|
||||||
|
l.addWidget(h, 0, 2, 1, 1)
|
||||||
|
|
||||||
|
h = QLabel(_('Values (see the popup help for more information)'))
|
||||||
|
h.setAlignment(Qt.AlignCenter)
|
||||||
|
h.setToolTip('<p>' +
|
||||||
|
_('You can enter more than one value per box, separated by commas. '
|
||||||
|
'The comparison ignores letter case. Special note: you can '
|
||||||
|
'enter at most one author.<br>'
|
||||||
|
'A value can be a regular expression. Check the box to turn '
|
||||||
'them on. When using regular expressions, note that the wizard '
|
'them on. When using regular expressions, note that the wizard '
|
||||||
'puts anchors (^ and $) around the expression, so you '
|
'puts anchors (^ and $) around the expression, so you '
|
||||||
'must ensure your expression matches from the beginning '
|
'must ensure your expression matches from the beginning '
|
||||||
'to the end of the tag.<br>'
|
'to the end of the column you are checking.<br>'
|
||||||
'Regular expression examples:') + '<ul>' +
|
'Regular expression examples:') + '<ul>' +
|
||||||
_('<li><code><b>.*</b></code> matches any tag. No empty tags are '
|
_('<li><code><b>.*</b></code> matches anything in the column. No '
|
||||||
'checked, so you don\'t need to worry about empty strings</li>'
|
'empty values are checked, so you don\'t need to worry about '
|
||||||
'<li><code><b>A.*</b></code> matches any tag beginning with A</li>'
|
'empty strings</li>'
|
||||||
'<li><code><b>.*mystery.*</b></code> matches any tag containing '
|
'<li><code><b>A.*</b></code> matches anything beginning with A</li>'
|
||||||
|
'<li><code><b>.*mystery.*</b></code> matches anything containing '
|
||||||
'the word "mystery"</li>') + '</ul></p>')
|
'the word "mystery"</li>') + '</ul></p>')
|
||||||
l.addWidget(h , 0, 0, 1, 1)
|
l.addWidget(h , 0, 3, 1, 1)
|
||||||
|
|
||||||
c = QLabel(_('is RE'))
|
c = QLabel(_('is RE'))
|
||||||
c.setToolTip('<p>' +
|
c.setToolTip('<p>' +
|
||||||
_('Check this box if the tag box contains regular expressions') + '</p>')
|
_('Check this box if the values box contains regular expressions') + '</p>')
|
||||||
l.addWidget(c, 0, 1, 1, 1)
|
l.addWidget(c, 0, 4, 1, 1)
|
||||||
|
|
||||||
c = QLabel(_('Color if tag found'))
|
c = QLabel(_('Color if value found'))
|
||||||
c.setToolTip('<p>' +
|
c.setToolTip('<p>' +
|
||||||
_('At least one of the two color boxes must have a value. Leave '
|
_('At least one of the two color boxes must have a value. Leave '
|
||||||
'one color box empty if you want the template to use the next '
|
'one color box empty if you want the template to use the next '
|
||||||
'line in this wizard. If both boxes are filled in, the rest of '
|
'line in this wizard. If both boxes are filled in, the rest of '
|
||||||
'the lines in this wizard will be ignored.') + '</p>')
|
'the lines in this wizard will be ignored.') + '</p>')
|
||||||
l.addWidget(c, 0, 2, 1, 1)
|
l.addWidget(c, 0, 5, 1, 1)
|
||||||
c = QLabel(_('Color if tag not found'))
|
c = QLabel(_('Color if value not found'))
|
||||||
c.setToolTip('<p>' +
|
c.setToolTip('<p>' +
|
||||||
_('This box is usually filled in only on the last test. If it is '
|
_('This box is usually filled in only on the last test. If it is '
|
||||||
'filled in before the last test, then the color for tag found box '
|
'filled in before the last test, then the color for value found box '
|
||||||
'must be empty or all the rest of the tests will be ignored.') + '</p>')
|
'must be empty or all the rest of the tests will be ignored.') + '</p>')
|
||||||
l.addWidget(c, 0, 3, 1, 1)
|
l.addWidget(c, 0, 6, 1, 1)
|
||||||
|
|
||||||
|
self.andboxes = []
|
||||||
|
self.notboxes = []
|
||||||
self.tagboxes = []
|
self.tagboxes = []
|
||||||
self.colorboxes = []
|
self.colorboxes = []
|
||||||
self.nfcolorboxes = []
|
self.nfcolorboxes = []
|
||||||
self.reboxes = []
|
self.reboxes = []
|
||||||
|
self.colboxes = []
|
||||||
self.colors = [unicode(s) for s in list(QColor.colorNames())]
|
self.colors = [unicode(s) for s in list(QColor.colorNames())]
|
||||||
self.colors.insert(0, '')
|
self.colors.insert(0, '')
|
||||||
for i in range(0, 10):
|
|
||||||
|
maxlines = 10
|
||||||
|
for i in range(1, maxlines+1):
|
||||||
|
ab = QCheckBox(self)
|
||||||
|
self.andboxes.append(ab)
|
||||||
|
if i != maxlines:
|
||||||
|
# let the last box float in space
|
||||||
|
l.addWidget(ab, i, 0, 2, 1)
|
||||||
|
ab.stateChanged.connect(partial(self.and_box_changed, line=i-1))
|
||||||
|
else:
|
||||||
|
ab.setVisible(False)
|
||||||
|
|
||||||
|
w = QComboBox(self)
|
||||||
|
w.addItems(self.columns)
|
||||||
|
l.addWidget(w, i, 1, 1, 1)
|
||||||
|
self.colboxes.append(w)
|
||||||
|
|
||||||
|
nb = QCheckBox(self)
|
||||||
|
self.notboxes.append(nb)
|
||||||
|
l.addWidget(nb, i, 2, 1, 1)
|
||||||
|
|
||||||
tb = MultiCompleteLineEdit(self)
|
tb = MultiCompleteLineEdit(self)
|
||||||
tb.set_separator(', ')
|
tb.set_separator(', ')
|
||||||
tb.update_items_cache(self.tags)
|
|
||||||
self.tagboxes.append(tb)
|
self.tagboxes.append(tb)
|
||||||
l.addWidget(tb, i+1, 0, 1, 1)
|
l.addWidget(tb, i, 3, 1, 1)
|
||||||
|
w.currentIndexChanged[str].connect(partial(self.column_changed, valbox=tb))
|
||||||
|
|
||||||
w = QCheckBox(self)
|
w = QCheckBox(self)
|
||||||
self.reboxes.append(w)
|
self.reboxes.append(w)
|
||||||
l.addWidget(w, i+1, 1, 1, 1)
|
l.addWidget(w, i, 4, 1, 1)
|
||||||
|
|
||||||
w = QComboBox(self)
|
w = QComboBox(self)
|
||||||
w.addItems(self.colors)
|
w.addItems(self.colors)
|
||||||
self.colorboxes.append(w)
|
self.colorboxes.append(w)
|
||||||
l.addWidget(w, i+1, 2, 1, 1)
|
l.addWidget(w, i, 5, 1, 1)
|
||||||
|
|
||||||
w = QComboBox(self)
|
w = QComboBox(self)
|
||||||
w.addItems(self.colors)
|
w.addItems(self.colors)
|
||||||
self.nfcolorboxes.append(w)
|
self.nfcolorboxes.append(w)
|
||||||
l.addWidget(w, i+1, 3, 1, 1)
|
l.addWidget(w, i, 6, 1, 1)
|
||||||
|
|
||||||
if txt:
|
if txt:
|
||||||
lines = txt.split('\n')[3:]
|
lines = txt.split('\n')[3:]
|
||||||
@ -141,39 +267,105 @@ class TagWizard(QDialog):
|
|||||||
t, c = vals
|
t, c = vals
|
||||||
nc = ''
|
nc = ''
|
||||||
re = False
|
re = False
|
||||||
|
f = 'tags'
|
||||||
|
a = False
|
||||||
|
n = False
|
||||||
else:
|
else:
|
||||||
t,c,nc,re = vals
|
t,c,f,nc,re,a,n = vals
|
||||||
try:
|
try:
|
||||||
self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c))
|
self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f))
|
||||||
self.nfcolorboxes[i].setCurrentIndex(self.nfcolorboxes[i].findText(nc))
|
self.colorboxes[i].setCurrentIndex(
|
||||||
|
self.colorboxes[i].findText(c))
|
||||||
|
self.nfcolorboxes[i].setCurrentIndex(
|
||||||
|
self.nfcolorboxes[i].findText(nc))
|
||||||
self.tagboxes[i].setText(t)
|
self.tagboxes[i].setText(t)
|
||||||
self.reboxes[i].setChecked(re == '2')
|
self.reboxes[i].setChecked(re == '2')
|
||||||
|
self.andboxes[i].setChecked(a == '2')
|
||||||
|
self.notboxes[i].setChecked(n == '2')
|
||||||
|
i += 1
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
i += 1
|
|
||||||
|
w = QLabel(_('Preview'))
|
||||||
|
l.addWidget(w, 99, 1, 1, 1)
|
||||||
|
w = self.test_box = QLineEdit(self)
|
||||||
|
w.setReadOnly(True)
|
||||||
|
l.addWidget(w, 99, 3, 1, 1)
|
||||||
|
w = QPushButton(_('Test'))
|
||||||
|
l.addWidget(w, 99, 5, 1, 1)
|
||||||
|
w.clicked.connect(self.preview)
|
||||||
|
|
||||||
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self)
|
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self)
|
||||||
l.addWidget(bb, 100, 2, 1, 2)
|
l.addWidget(bb, 100, 3, 1, 2)
|
||||||
bb.accepted.connect(self.accepted)
|
bb.accepted.connect(self.accepted)
|
||||||
bb.rejected.connect(self.reject)
|
bb.rejected.connect(self.reject)
|
||||||
self.template = ''
|
self.template = ''
|
||||||
|
|
||||||
def accepted(self):
|
def preview(self):
|
||||||
|
if not self.generate_program():
|
||||||
|
return
|
||||||
|
t = composite_formatter.safe_format(self.template, self.mi,
|
||||||
|
_('EXCEPTION'), self.mi)
|
||||||
|
self.test_box.setText(t)
|
||||||
|
|
||||||
|
def column_changed(self, s, valbox=None):
|
||||||
|
k = unicode(s)
|
||||||
|
if k in self.completion_values:
|
||||||
|
valbox.update_items_cache(self.completion_values[k]['v'])
|
||||||
|
if self.completion_values[k]['m']:
|
||||||
|
valbox.set_separator(', ')
|
||||||
|
else:
|
||||||
|
valbox.set_separator(None)
|
||||||
|
else:
|
||||||
|
valbox.update_items_cache([])
|
||||||
|
valbox.set_separator(None)
|
||||||
|
|
||||||
|
def generate_program(self):
|
||||||
res = ("program:\n#tag wizard -- do not directly edit\n"
|
res = ("program:\n#tag wizard -- do not directly edit\n"
|
||||||
" t = field('tags');\n first_non_empty(\n")
|
" first_non_empty(\n")
|
||||||
lines = []
|
lines = []
|
||||||
for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
|
was_and = False
|
||||||
self.nfcolorboxes, self.reboxes):
|
had_line = False
|
||||||
tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()]
|
|
||||||
|
line = 0
|
||||||
|
for tb, cb, fb, nfcb, reb, ab, nb in zip(
|
||||||
|
self.tagboxes, self.colorboxes, self.colboxes,
|
||||||
|
self.nfcolorboxes, self.reboxes, self.andboxes, self.notboxes):
|
||||||
|
f = unicode(fb.currentText())
|
||||||
|
if not f:
|
||||||
|
continue
|
||||||
|
m = self.completion_values[f]['m']
|
||||||
c = unicode(cb.currentText()).strip()
|
c = unicode(cb.currentText()).strip()
|
||||||
nfc = unicode(nfcb.currentText()).strip()
|
nfc = unicode(nfcb.currentText()).strip()
|
||||||
re = reb.checkState()
|
re = reb.checkState()
|
||||||
if re == 2:
|
a = ab.checkState()
|
||||||
tags = '$|^'.join(tags)
|
n = nb.checkState()
|
||||||
|
line += 1
|
||||||
|
|
||||||
|
if n == 2:
|
||||||
|
tval = ''
|
||||||
|
fval = '1'
|
||||||
else:
|
else:
|
||||||
tags = ','.join(tags)
|
tval = '1'
|
||||||
if not tags or not (c or nfc):
|
fval = ''
|
||||||
continue
|
|
||||||
|
if m:
|
||||||
|
tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()]
|
||||||
|
if re == 2:
|
||||||
|
tags = '$|^'.join(tags)
|
||||||
|
else:
|
||||||
|
tags = ','.join(tags)
|
||||||
|
else:
|
||||||
|
tags = unicode(tb.text()).strip()
|
||||||
|
if f == 'authors':
|
||||||
|
tags.replace(',', '|')
|
||||||
|
|
||||||
|
if (tags or f) and not (tags and f and (a == 2 or c)):
|
||||||
|
error_dialog(self, _('Invalid line'),
|
||||||
|
_('Line number {0} is not valid').format(line),
|
||||||
|
show=True, show_copy_button=False)
|
||||||
|
return False
|
||||||
|
|
||||||
if c not in self.colors:
|
if c not in self.colors:
|
||||||
error_dialog(self, _('Invalid color'),
|
error_dialog(self, _('Invalid color'),
|
||||||
_('The color {0} is not valid').format(c),
|
_('The color {0} is not valid').format(c),
|
||||||
@ -184,25 +376,69 @@ class TagWizard(QDialog):
|
|||||||
_('The color {0} is not valid').format(nfc),
|
_('The color {0} is not valid').format(nfc),
|
||||||
show=True, show_copy_button=False)
|
show=True, show_copy_button=False)
|
||||||
return False
|
return False
|
||||||
if re == 2:
|
|
||||||
lines.append(" in_list(t, ',', '^{0}$', '{1}', '{2}')".\
|
if not was_and:
|
||||||
format(tags, c, nfc))
|
if had_line:
|
||||||
|
lines[-1] += ','
|
||||||
|
had_line = True
|
||||||
|
lines.append(" test(and(")
|
||||||
else:
|
else:
|
||||||
lines.append(" str_in_list(t, ',', '{0}', '{1}', '{2}')".\
|
lines[-1] += ','
|
||||||
format(tags, c, nfc))
|
|
||||||
res += ',\n'.join(lines)
|
if re == 2:
|
||||||
|
if m:
|
||||||
|
lines.append(" in_list(field('{1}'), ',', '^{0}$', '{2}', '{3}')".\
|
||||||
|
format(tags, f, tval, fval))
|
||||||
|
else:
|
||||||
|
lines.append(" contains(field('{1}'), '{0}', '{2}', '{3}')".\
|
||||||
|
format(tags, f, tval, fval))
|
||||||
|
else:
|
||||||
|
if m:
|
||||||
|
lines.append(" str_in_list(field('{1}'), ',', '{0}', '{2}', '{3}')".\
|
||||||
|
format(tags, f, tval, fval))
|
||||||
|
else:
|
||||||
|
lines.append(" strcmp(field('{1}'), '{0}', '{3}', '{2}', '{3}')".\
|
||||||
|
format(tags, f, tval, fval))
|
||||||
|
if a == 2:
|
||||||
|
was_and = True
|
||||||
|
else:
|
||||||
|
was_and = False
|
||||||
|
lines.append(" ), '{0}', '{1}')".format(c, nfc))
|
||||||
|
|
||||||
|
res += '\n'.join(lines)
|
||||||
res += ')\n'
|
res += ')\n'
|
||||||
self.template = res
|
self.template = res
|
||||||
res = ''
|
res = ''
|
||||||
for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
|
for tb, cb, fb, nfcb, reb, ab, nb in zip(
|
||||||
self.nfcolorboxes, self.reboxes):
|
self.tagboxes, self.colorboxes, self.colboxes,
|
||||||
|
self.nfcolorboxes, self.reboxes, self.andboxes, self.notboxes):
|
||||||
t = unicode(tb.text()).strip()
|
t = unicode(tb.text()).strip()
|
||||||
if t.endswith(','):
|
if t.endswith(','):
|
||||||
t = t[:-1]
|
t = t[:-1]
|
||||||
c = unicode(cb.currentText()).strip()
|
c = unicode(cb.currentText()).strip()
|
||||||
|
f = unicode(fb.currentText())
|
||||||
nfc = unicode(nfcb.currentText()).strip()
|
nfc = unicode(nfcb.currentText()).strip()
|
||||||
re = unicode(reb.checkState())
|
re = unicode(reb.checkState())
|
||||||
if t and c:
|
a = unicode(ab.checkState())
|
||||||
res += '#' + t + ':|:' + c + ':|:' + nfc + ':|:' + re + '\n'
|
n = unicode(nb.checkState())
|
||||||
|
if f and t and (a == '2' or c):
|
||||||
|
res += '#' + t + ':|:' + c + ':|:' + f + ':|:' + \
|
||||||
|
nfc + ':|:' + re + ':|:' + a + ':|:' + n + '\n'
|
||||||
self.template += res
|
self.template += res
|
||||||
self.accept()
|
return True
|
||||||
|
|
||||||
|
def and_box_changed(self, state, line=None):
|
||||||
|
if state == 2:
|
||||||
|
self.colorboxes[line].setCurrentIndex(0)
|
||||||
|
self.colorboxes[line].setEnabled(False)
|
||||||
|
self.nfcolorboxes[line].setCurrentIndex(0)
|
||||||
|
self.nfcolorboxes[line].setEnabled(False)
|
||||||
|
else:
|
||||||
|
self.colorboxes[line].setEnabled(True)
|
||||||
|
self.nfcolorboxes[line].setEnabled(True)
|
||||||
|
|
||||||
|
def accepted(self):
|
||||||
|
if self.generate_program():
|
||||||
|
self.accept()
|
||||||
|
else:
|
||||||
|
self.template = ''
|
||||||
|
@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import shutil, functools, re, os, traceback
|
import shutil, functools, re, os, traceback
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage,
|
from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage,
|
||||||
QModelIndex, QVariant, QDate, QColor)
|
QModelIndex, QVariant, QDate, QColor)
|
||||||
@ -87,6 +88,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.column_map = []
|
self.column_map = []
|
||||||
self.headers = {}
|
self.headers = {}
|
||||||
self.alignment_map = {}
|
self.alignment_map = {}
|
||||||
|
self.color_cache = defaultdict(dict)
|
||||||
self.buffer_size = buffer
|
self.buffer_size = buffer
|
||||||
self.metadata_backup = None
|
self.metadata_backup = None
|
||||||
self.bool_yes_icon = QIcon(I('ok.png'))
|
self.bool_yes_icon = QIcon(I('ok.png'))
|
||||||
@ -97,7 +99,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.ids_to_highlight_set = set()
|
self.ids_to_highlight_set = set()
|
||||||
self.current_highlighted_idx = None
|
self.current_highlighted_idx = None
|
||||||
self.highlight_only = False
|
self.highlight_only = False
|
||||||
self.column_color_map = {}
|
self.column_color_list = []
|
||||||
self.colors = [unicode(c) for c in QColor.colorNames()]
|
self.colors = [unicode(c) for c in QColor.colorNames()]
|
||||||
self.read_config()
|
self.read_config()
|
||||||
|
|
||||||
@ -172,11 +174,13 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
|
|
||||||
|
|
||||||
def refresh_ids(self, ids, current_row=-1):
|
def refresh_ids(self, ids, current_row=-1):
|
||||||
|
self.color_cache = defaultdict(dict)
|
||||||
rows = self.db.refresh_ids(ids)
|
rows = self.db.refresh_ids(ids)
|
||||||
if rows:
|
if rows:
|
||||||
self.refresh_rows(rows, current_row=current_row)
|
self.refresh_rows(rows, current_row=current_row)
|
||||||
|
|
||||||
def refresh_rows(self, rows, current_row=-1):
|
def refresh_rows(self, rows, current_row=-1):
|
||||||
|
self.color_cache = defaultdict(dict)
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if row == current_row:
|
if row == current_row:
|
||||||
self.new_bookdisplay_data.emit(
|
self.new_bookdisplay_data.emit(
|
||||||
@ -206,6 +210,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def count_changed(self, *args):
|
def count_changed(self, *args):
|
||||||
|
self.color_cache = defaultdict(dict)
|
||||||
self.count_changed_signal.emit(self.db.count())
|
self.count_changed_signal.emit(self.db.count())
|
||||||
|
|
||||||
def row_indices(self, index):
|
def row_indices(self, index):
|
||||||
@ -336,6 +341,10 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.db.refresh(field=None)
|
self.db.refresh(field=None)
|
||||||
self.resort(reset=reset)
|
self.resort(reset=reset)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.color_cache = defaultdict(dict)
|
||||||
|
QAbstractTableModel.reset(self)
|
||||||
|
|
||||||
def resort(self, reset=True):
|
def resort(self, reset=True):
|
||||||
if not self.db:
|
if not self.db:
|
||||||
return
|
return
|
||||||
@ -537,12 +546,12 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
return img
|
return img
|
||||||
|
|
||||||
def set_color_templates(self, reset=True):
|
def set_color_templates(self, reset=True):
|
||||||
self.column_color_map = {}
|
self.column_color_list = []
|
||||||
for i in range(1,self.db.column_color_count+1):
|
for i in range(1,self.db.column_color_count+1):
|
||||||
name = self.db.prefs.get('column_color_name_'+str(i))
|
name = self.db.prefs.get('column_color_name_'+str(i))
|
||||||
if name:
|
if name:
|
||||||
self.column_color_map[name] = \
|
self.column_color_list.append((name,
|
||||||
self.db.prefs.get('column_color_template_'+str(i))
|
self.db.prefs.get('column_color_template_'+str(i))))
|
||||||
if reset:
|
if reset:
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
@ -717,18 +726,25 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
return QVariant(QColor('lightgreen'))
|
return QVariant(QColor('lightgreen'))
|
||||||
elif role == Qt.ForegroundRole:
|
elif role == Qt.ForegroundRole:
|
||||||
key = self.column_map[col]
|
key = self.column_map[col]
|
||||||
if key in self.column_color_map:
|
for k,fmt in self.column_color_list:
|
||||||
|
if k != key:
|
||||||
|
continue
|
||||||
|
id_ = self.id(index)
|
||||||
|
if id_ in self.color_cache:
|
||||||
|
if key in self.color_cache[id_]:
|
||||||
|
return self.color_cache[id_][key]
|
||||||
mi = self.db.get_metadata(self.id(index), index_is_id=True)
|
mi = self.db.get_metadata(self.id(index), index_is_id=True)
|
||||||
fmt = self.column_color_map[key]
|
|
||||||
try:
|
try:
|
||||||
color = composite_formatter.safe_format(fmt, mi, '', mi)
|
color = composite_formatter.safe_format(fmt, mi, '', mi)
|
||||||
if color in self.colors:
|
if color in self.colors:
|
||||||
color = QColor(color)
|
color = QColor(color)
|
||||||
if color.isValid():
|
if color.isValid():
|
||||||
return QVariant(color)
|
color = QVariant(color)
|
||||||
|
self.color_cache[id_][key] = color
|
||||||
|
return color
|
||||||
except:
|
except:
|
||||||
return NONE
|
return NONE
|
||||||
elif self.is_custom_column(key) and \
|
if self.is_custom_column(key) and \
|
||||||
self.custom_columns[key]['datatype'] == 'enumeration':
|
self.custom_columns[key]['datatype'] == 'enumeration':
|
||||||
cc = self.custom_columns[self.column_map[col]]['display']
|
cc = self.custom_columns[self.column_map[col]]['display']
|
||||||
colors = cc.get('enum_colors', [])
|
colors = cc.get('enum_colors', [])
|
||||||
|
@ -5,12 +5,15 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import (QApplication, QFont, QFontInfo, QFontDialog,
|
from PyQt4.Qt import (QApplication, QFont, QFontInfo, QFontDialog,
|
||||||
QAbstractListModel, Qt, QColor)
|
QAbstractListModel, Qt, QColor, QIcon, QToolButton, QComboBox)
|
||||||
|
|
||||||
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList
|
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList
|
||||||
from calibre.gui2.preferences.look_feel_ui import Ui_Form
|
from calibre.gui2.preferences.look_feel_ui import Ui_Form
|
||||||
from calibre.gui2 import config, gprefs, qt_app
|
from calibre.gui2 import config, gprefs, qt_app
|
||||||
|
from calibre.gui2.dialogs.template_line_editor import TemplateLineEditor
|
||||||
from calibre.utils.localization import (available_translations,
|
from calibre.utils.localization import (available_translations,
|
||||||
get_language, get_lang)
|
get_language, get_lang)
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
@ -167,14 +170,15 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
'<a href="http://manual.calibre-ebook.com/template_lang.html">'
|
'<a href="http://manual.calibre-ebook.com/template_lang.html">'
|
||||||
'tutorial</a> on using templates.') +
|
'tutorial</a> on using templates.') +
|
||||||
'</p><p>' +
|
'</p><p>' +
|
||||||
_('If you want to color a field based on tags, then click the '
|
_('If you want to color a field based on contents of columns, '
|
||||||
'button next to an empty line to open the tags wizard. '
|
'then click the button next to an empty line to open the wizard. '
|
||||||
'It will build a template for you. You can later edit that '
|
'It will build a template for you. You can later edit that '
|
||||||
'template with the same wizard. If you edit it by hand, the '
|
'template with the same wizard. This is by far the easiest '
|
||||||
'wizard might not work or might restore old values.') +
|
'way to specify a template.') +
|
||||||
'</p><p>' +
|
'</p><p>' +
|
||||||
_('The template must evaluate to one of the color names shown '
|
_('If you manually construct a template, then the template must '
|
||||||
'below. You can use any legal template expression. '
|
'evaluate to a valid color name shown in the color names box.'
|
||||||
|
'You can use any legal template expression. '
|
||||||
'For example, you can set the title to always display in '
|
'For example, you can set the title to always display in '
|
||||||
'green using the template "green" (without the quotes). '
|
'green using the template "green" (without the quotes). '
|
||||||
'To show the title in the color named in the custom column '
|
'To show the title in the color named in the custom column '
|
||||||
@ -200,11 +204,16 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
'of values", it is often easier to specify the '
|
'of values", it is often easier to specify the '
|
||||||
'colors in the column definition dialog. There you can '
|
'colors in the column definition dialog. There you can '
|
||||||
'provide a color for each value without using a template.')+ '</p>')
|
'provide a color for each value without using a template.')+ '</p>')
|
||||||
|
self.color_help_scrollArea.setVisible(False)
|
||||||
|
self.color_help_button.clicked.connect(self.change_help_text)
|
||||||
|
self.colors_scrollArea.setVisible(False)
|
||||||
|
self.colors_label.setVisible(False)
|
||||||
|
self.colors_button.clicked.connect(self.change_colors_text)
|
||||||
|
|
||||||
choices = db.field_metadata.displayable_field_keys()
|
choices = db.field_metadata.displayable_field_keys()
|
||||||
choices.sort(key=sort_key)
|
choices.sort(key=sort_key)
|
||||||
choices.insert(0, '')
|
choices.insert(0, '')
|
||||||
self.column_color_count = db.column_color_count+1
|
self.column_color_count = db.column_color_count+1
|
||||||
tags = db.all_tags()
|
|
||||||
|
|
||||||
mi=None
|
mi=None
|
||||||
try:
|
try:
|
||||||
@ -213,17 +222,58 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
l = self.column_color_layout
|
||||||
for i in range(1, self.column_color_count):
|
for i in range(1, self.column_color_count):
|
||||||
|
ccn = QComboBox(parent=self)
|
||||||
|
setattr(self, 'opt_column_color_name_'+str(i), ccn)
|
||||||
|
l.addWidget(ccn, i, 0, 1, 1)
|
||||||
|
|
||||||
|
wtb = QToolButton(parent=self)
|
||||||
|
setattr(self, 'opt_column_color_wizard_'+str(i), wtb)
|
||||||
|
wtb.setIcon(QIcon(I('wizard.png')))
|
||||||
|
l.addWidget(wtb, i, 1, 1, 1)
|
||||||
|
|
||||||
|
ttb = QToolButton(parent=self)
|
||||||
|
setattr(self, 'opt_column_color_tpledit_'+str(i), ttb)
|
||||||
|
ttb.setIcon(QIcon(I('edit_input.png')))
|
||||||
|
l.addWidget(ttb, i, 2, 1, 1)
|
||||||
|
|
||||||
|
tpl = TemplateLineEditor(parent=self)
|
||||||
|
setattr(self, 'opt_column_color_template_'+str(i), tpl)
|
||||||
|
tpl.textChanged.connect(partial(self.tpl_edit_text_changed, ctrl=i))
|
||||||
|
tpl.set_db(db)
|
||||||
|
tpl.set_mi(mi)
|
||||||
|
l.addWidget(tpl, i, 3, 1, 1)
|
||||||
|
|
||||||
|
wtb.clicked.connect(tpl.tag_wizard)
|
||||||
|
ttb.clicked.connect(tpl.open_editor)
|
||||||
|
|
||||||
r('column_color_name_'+str(i), db.prefs, choices=choices)
|
r('column_color_name_'+str(i), db.prefs, choices=choices)
|
||||||
r('column_color_template_'+str(i), db.prefs)
|
r('column_color_template_'+str(i), db.prefs)
|
||||||
tpl = getattr(self, 'opt_column_color_template_'+str(i))
|
txt = db.prefs.get('column_color_template_'+str(i), None)
|
||||||
tpl.set_tags(tags)
|
|
||||||
tpl.set_mi(mi)
|
wtb.setEnabled(tpl.enable_wizard_button(txt))
|
||||||
toolbutton = getattr(self, 'opt_column_color_wizard_'+str(i))
|
ttb.setEnabled(not tpl.enable_wizard_button(txt) or not txt)
|
||||||
toolbutton.clicked.connect(tpl.tag_wizard)
|
|
||||||
all_colors = [unicode(s) for s in list(QColor.colorNames())]
|
all_colors = [unicode(s) for s in list(QColor.colorNames())]
|
||||||
self.colors_box.setText(', '.join(all_colors))
|
self.colors_box.setText(', '.join(all_colors))
|
||||||
|
|
||||||
|
def change_help_text(self):
|
||||||
|
self.color_help_scrollArea.setVisible(not self.color_help_scrollArea.isVisible())
|
||||||
|
|
||||||
|
def change_colors_text(self):
|
||||||
|
self.colors_scrollArea.setVisible(not self.colors_scrollArea.isVisible())
|
||||||
|
self.colors_label.setVisible(not self.colors_label.isVisible())
|
||||||
|
|
||||||
|
def tpl_edit_text_changed(self, ign, ctrl=None):
|
||||||
|
tpl = getattr(self, 'opt_column_color_template_'+str(ctrl))
|
||||||
|
txt = unicode(tpl.text())
|
||||||
|
wtb = getattr(self, 'opt_column_color_wizard_'+str(ctrl))
|
||||||
|
ttb = getattr(self, 'opt_column_color_tpledit_'+str(ctrl))
|
||||||
|
wtb.setEnabled(tpl.enable_wizard_button(txt))
|
||||||
|
ttb.setEnabled(not tpl.enable_wizard_button(txt) or not txt)
|
||||||
|
tpl.setFocus()
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
ConfigWidgetBase.initialize(self)
|
ConfigWidgetBase.initialize(self)
|
||||||
font = gprefs['font']
|
font = gprefs['font']
|
||||||
|
@ -416,114 +416,95 @@ then the tags will be displayed each on their own line.</string>
|
|||||||
<string>Column Coloring</string>
|
<string>Column Coloring</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="column_color_layout">
|
<layout class="QGridLayout" name="column_color_layout">
|
||||||
<item row="1" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Column to color</string>
|
<string>Column to color</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="0" column="3">
|
||||||
<widget class="QLabel" name="label">
|
<layout class="QHBoxLayout">
|
||||||
<property name="text">
|
<item>
|
||||||
<string>Color selection template</string>
|
<widget class="QLabel" name="label">
|
||||||
</property>
|
<property name="text">
|
||||||
</widget>
|
<string>Color selection template</string>
|
||||||
</item>
|
</property>
|
||||||
<item row="2" column="0">
|
<property name="sizePolicy">
|
||||||
<widget class="QComboBox" name="opt_column_color_name_1"/>
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
</item>
|
<horstretch>10</horstretch>
|
||||||
<item row="2" column="1">
|
<verstretch>0</verstretch>
|
||||||
<widget class="TemplateLineEditor" name="opt_column_color_template_1"/>
|
</sizepolicy>
|
||||||
</item>
|
</property>
|
||||||
<item row="2" column="2">
|
</widget>
|
||||||
<widget class="QToolButton" name="opt_column_color_wizard_1">
|
</item>
|
||||||
<property name="icon">
|
<item>
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
<widget class="QLabel" name="label">
|
||||||
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
|
<property name="text">
|
||||||
</property>
|
<string>The template wizard is easiest to use</string>
|
||||||
<property name="toolTip">
|
</property>
|
||||||
<string>Open the tags wizard.</string>
|
</widget>
|
||||||
</property>
|
</item>
|
||||||
</widget>
|
<item>
|
||||||
</item>
|
<widget class="QPushButton" name="color_help_button">
|
||||||
<item row="3" column="0">
|
<property name="text">
|
||||||
<widget class="QComboBox" name="opt_column_color_name_2"/>
|
<string>Show/hide help text</string>
|
||||||
</item>
|
</property>
|
||||||
<item row="3" column="1">
|
</widget>
|
||||||
<widget class="TemplateLineEditor" name="opt_column_color_template_2"/>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
<item row="3" column="2">
|
<widget class="QPushButton" name="colors_button">
|
||||||
<widget class="QToolButton" name="opt_column_color_wizard_2">
|
<property name="text">
|
||||||
<property name="icon">
|
<string>Show/hide colors</string>
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
</property>
|
||||||
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
|
</widget>
|
||||||
</property>
|
</item>
|
||||||
<property name="toolTip">
|
</layout>
|
||||||
<string>Open the tags wizard.</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QComboBox" name="opt_column_color_name_3"/>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="TemplateLineEditor" name="opt_column_color_template_3"/>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="2">
|
|
||||||
<widget class="QToolButton" name="opt_column_color_wizard_3">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Open the tags wizard.</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QComboBox" name="opt_column_color_name_4"/>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<widget class="TemplateLineEditor" name="opt_column_color_template_4"/>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="2">
|
|
||||||
<widget class="QToolButton" name="opt_column_color_wizard_4">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Open the tags wizard.</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="0">
|
|
||||||
<widget class="QComboBox" name="opt_column_color_name_5"/>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="1">
|
|
||||||
<widget class="TemplateLineEditor" name="opt_column_color_template_5"/>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="2">
|
|
||||||
<widget class="QToolButton" name="opt_column_color_wizard_5">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Open the tags wizard.</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="20" column="0">
|
<item row="20" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="colors_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Color names</string>
|
<string>Color names</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0" colspan="3">
|
<item row="21" column="0" colspan="8">
|
||||||
<widget class="QScrollArea" name="scrollArea">
|
<widget class="QScrollArea" name="colors_scrollArea">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>300</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>687</width>
|
||||||
|
<height>61</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="colors_box">
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="30" column="0" colspan="8">
|
||||||
|
<widget class="QScrollArea" name="color_help_scrollArea">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
@ -534,7 +515,7 @@ then the tags will be displayed each on their own line.</string>
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
@ -560,37 +541,24 @@ then the tags will be displayed each on their own line.</string>
|
|||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="21" column="0" colspan="3">
|
<item row="40" column="0">
|
||||||
<widget class="QScrollArea" name="scrollArea_2">
|
<spacer>
|
||||||
<property name="maximumSize">
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy vsizetype="Expanding" hsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>10</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>16777215</width>
|
<width>0</width>
|
||||||
<height>120</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="widgetResizable">
|
</spacer>
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="scrollAreaWidgetContents_2">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>687</width>
|
|
||||||
<height>61</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="colors_box">
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -352,7 +352,7 @@ The syntax for searching for dates is::
|
|||||||
If the date is ambiguous, the current locale is used for date comparison. For example, in an mm/dd/yyyy
|
If the date is ambiguous, the current locale is used for date comparison. For example, in an mm/dd/yyyy
|
||||||
locale, 2/1/2009 is interpreted as 1 Feb 2009. In a dd/mm/yyyy locale, it is interpreted as 2 Jan 2009. Some
|
locale, 2/1/2009 is interpreted as 1 Feb 2009. In a dd/mm/yyyy locale, it is interpreted as 2 Jan 2009. Some
|
||||||
special date strings are available. The string ``today`` translates to today's date, whatever it is. The
|
special date strings are available. The string ``today`` translates to today's date, whatever it is. The
|
||||||
strings `yesterday`` and ``thismonth`` also work. In addition, the string ``daysago`` can be used to compare
|
strings ``yesterday`` and ``thismonth`` also work. In addition, the string ``daysago`` can be used to compare
|
||||||
to a date some number of days ago, for example: date:>10daysago, date:<=45daysago.
|
to a date some number of days ago, for example: date:>10daysago, date:<=45daysago.
|
||||||
|
|
||||||
You can search for books that have a format of a certain size like this::
|
You can search for books that have a format of a certain size like this::
|
||||||
|
@ -122,7 +122,7 @@ The functions available are:
|
|||||||
* ``uppercase()`` -- return the value of the field in upper case.
|
* ``uppercase()`` -- return the value of the field in upper case.
|
||||||
* ``titlecase()`` -- return the value of the field in title case.
|
* ``titlecase()`` -- return the value of the field in title case.
|
||||||
* ``capitalize()`` -- return the value with the first letter upper case and the rest lower case.
|
* ``capitalize()`` -- return the value with the first letter upper case and the rest lower case.
|
||||||
* ``contains(pattern, text if match, text if not match`` -- checks if field contains matches for the regular expression `pattern`. Returns `text if match` if matches are found, otherwise it returns `text if no match`.
|
* ``contains(pattern, text if match, text if not match)`` -- checks if field contains matches for the regular expression `pattern`. Returns `text if match` if matches are found, otherwise it returns `text if no match`.
|
||||||
* ``count(separator)`` -- interprets the value as a list of items separated by `separator`, returning the number of items in the list. Most lists use a comma as the separator, but authors uses an ampersand. Examples: `{tags:count(,)}`, `{authors:count(&)}`
|
* ``count(separator)`` -- interprets the value as a list of items separated by `separator`, returning the number of items in the list. Most lists use a comma as the separator, but authors uses an ampersand. Examples: `{tags:count(,)}`, `{authors:count(&)}`
|
||||||
* ``ifempty(text)`` -- if the field is not empty, return the value of the field. Otherwise return `text`.
|
* ``ifempty(text)`` -- if the field is not empty, return the value of the field. Otherwise return `text`.
|
||||||
* ``in_list(separator, pattern, found_val, not_found_val)`` -- interpret the field as a list of items separated by `separator`, comparing the `pattern` against each value in the list. If the pattern matches a value, return `found_val`, otherwise return `not_found_val`.
|
* ``in_list(separator, pattern, found_val, not_found_val)`` -- interpret the field as a list of items separated by `separator`, comparing the `pattern` against each value in the list. If the pattern matches a value, return `found_val`, otherwise return `not_found_val`.
|
||||||
|
@ -331,9 +331,10 @@ class BuiltinInList(BuiltinFormatterFunction):
|
|||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):
|
def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):
|
||||||
l = [v.strip() for v in val.split(sep) if v.strip()]
|
l = [v.strip() for v in val.split(sep) if v.strip()]
|
||||||
for v in l:
|
if l:
|
||||||
if re.search(pat, v, flags=re.I):
|
for v in l:
|
||||||
return fv
|
if re.search(pat, v, flags=re.I):
|
||||||
|
return fv
|
||||||
return nfv
|
return nfv
|
||||||
|
|
||||||
class BuiltinStrInList(BuiltinFormatterFunction):
|
class BuiltinStrInList(BuiltinFormatterFunction):
|
||||||
@ -349,10 +350,11 @@ class BuiltinStrInList(BuiltinFormatterFunction):
|
|||||||
def evaluate(self, formatter, kwargs, mi, locals, val, sep, str, fv, nfv):
|
def evaluate(self, formatter, kwargs, mi, locals, val, sep, str, fv, nfv):
|
||||||
l = [v.strip() for v in val.split(sep) if v.strip()]
|
l = [v.strip() for v in val.split(sep) if v.strip()]
|
||||||
c = [v.strip() for v in str.split(sep) if v.strip()]
|
c = [v.strip() for v in str.split(sep) if v.strip()]
|
||||||
for v in l:
|
if l:
|
||||||
for t in c:
|
for v in l:
|
||||||
if strcmp(t, v) == 0:
|
for t in c:
|
||||||
return fv
|
if strcmp(t, v) == 0:
|
||||||
|
return fv
|
||||||
return nfv
|
return nfv
|
||||||
|
|
||||||
class BuiltinRe(BuiltinFormatterFunction):
|
class BuiltinRe(BuiltinFormatterFunction):
|
||||||
|
@ -1123,6 +1123,13 @@ class ZipFile:
|
|||||||
targetpath = os.sep.join(components)
|
targetpath = os.sep.join(components)
|
||||||
with open(targetpath, 'wb') as target:
|
with open(targetpath, 'wb') as target:
|
||||||
shutil.copyfileobj(source, target)
|
shutil.copyfileobj(source, target)
|
||||||
|
# Kovid: Try to preserve the timestamps in the ZIP file
|
||||||
|
try:
|
||||||
|
mtime = time.localtime()
|
||||||
|
mtime = time.mktime(member.date_time + (0, 0) + (mtime.tm_isdst,))
|
||||||
|
os.utime(targetpath, (mtime, mtime))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
self.extract_mapping[member.filename] = targetpath
|
self.extract_mapping[member.filename] = targetpath
|
||||||
return targetpath
|
return targetpath
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user