mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Pull from trunk
This commit is contained in:
commit
3a06faa17d
@ -4,6 +4,64 @@
|
|||||||
# for important features/bug fixes.
|
# for important features/bug fixes.
|
||||||
# Also, each release can have new and improved recipes.
|
# Also, each release can have new and improved recipes.
|
||||||
|
|
||||||
|
- version: 0.7.14
|
||||||
|
date: 2010-08-13
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Device drivers for the: Teclast K-5, Samsung SNE-60 and Samsung i7500"
|
||||||
|
|
||||||
|
- title: "When showing cover browser in a separate window, remember the last used window size"
|
||||||
|
|
||||||
|
- title: "Add keyboard shortcuts to show/hide the Tag Browser, Book details and Cover Browser panels. Hover your mouse over the buttons that toggle them to see the shortcuts."
|
||||||
|
|
||||||
|
- title: "Calibre library: When the case of title or author is changed, automatically rename the folders to reflect the new case, even on case insensitive filesystems"
|
||||||
|
|
||||||
|
- title: "Metadata download: If downloaded title or author is all upper case, automatically fix the case"
|
||||||
|
|
||||||
|
- title: "Add method to add books by ISBN. Click the arrow next to Add Books to add from a list of ISBNs."
|
||||||
|
tickets: [6327]
|
||||||
|
|
||||||
|
- title: "Allow editing of tweaks via Preferences->Advanced"
|
||||||
|
|
||||||
|
- title: "Add button to manage authors dialog to automatically reset all author sort values"
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Fix regression in 0.7.13 that broke changing libraries"
|
||||||
|
|
||||||
|
- title: "MOBI Output: When processing an input document that specifies non-existant files in the OPF guide, don't crash."
|
||||||
|
tickets: [6490]
|
||||||
|
|
||||||
|
- title: "E-book viewer: When opening consecutive documents in the same viewer, show the correct title in the titlebar"
|
||||||
|
|
||||||
|
- title: "Set screen size to 540x718 in Kobo output profile"
|
||||||
|
|
||||||
|
- title: "Dont allow calibredb to create custom columns with invalid labels."
|
||||||
|
tickets: [6487]
|
||||||
|
|
||||||
|
- title: "Fix preference to 'search as you type' not working"
|
||||||
|
|
||||||
|
- title: "iTunes driver: Fixed bug in PDF file name searching after adding to iTunes database (Windows only)"
|
||||||
|
|
||||||
|
- title: "Displaying HTML comments: Do not start a new paragraph at the period in words like Ph.D"
|
||||||
|
tickets: [6462]
|
||||||
|
|
||||||
|
- title: "Respect restriction in effect when refreshing book list"
|
||||||
|
|
||||||
|
- title: "Fix drives being reversed for softrooted nook"
|
||||||
|
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "Yahoo News, Skeptical Enquirer and Skeptic"
|
||||||
|
author: Startson17
|
||||||
|
|
||||||
|
- title: "Bolivian newspapers"
|
||||||
|
author: Darko Miletic
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Esquire
|
||||||
|
- Big Oven
|
||||||
|
- NSPM
|
||||||
|
|
||||||
- version: 0.7.13
|
- version: 0.7.13
|
||||||
date: 2010-08-06
|
date: 2010-08-06
|
||||||
|
|
||||||
|
BIN
resources/images/news/fe_india.png
Normal file
BIN
resources/images/news/fe_india.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 370 B |
@ -1,4 +1,5 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
import re
|
||||||
|
|
||||||
class BigOven(BasicNewsRecipe):
|
class BigOven(BasicNewsRecipe):
|
||||||
title = 'BigOven'
|
title = 'BigOven'
|
||||||
@ -26,39 +27,38 @@ class BigOven(BasicNewsRecipe):
|
|||||||
def get_browser(self):
|
def get_browser(self):
|
||||||
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.bigoven.com/')
|
br.open('http://www.bigoven.com/account/login?ReturnUrl=/')
|
||||||
br.select_form(name='form1')
|
br.select_form(nr=1)
|
||||||
br['TopMenu_bo1$email'] = self.username
|
br['Email'] = self.username
|
||||||
br['TopMenu_bo1$password'] = self.password
|
br['Password'] = self.password
|
||||||
br.submit()
|
br.submit()
|
||||||
return br
|
return br
|
||||||
|
|
||||||
remove_attributes = ['style', 'font']
|
remove_attributes = ['style', 'font']
|
||||||
|
|
||||||
keep_only_tags = [dict(name='h1')
|
remove_tags = [dict(name='div', attrs={'class':['ppy-caption']})
|
||||||
,dict(name='div', attrs={'class':'img'})
|
,dict(name='div', attrs={'id':['float_corner']})
|
||||||
,dict(name='div', attrs={'id':'intro'})
|
|
||||||
]
|
]
|
||||||
|
|
||||||
remove_tags = [dict(name='div', attrs={'style':["overflow: visible;"]})
|
def preprocess_html(self, soup):
|
||||||
,dict(name='div', attrs={'class':['ctas']})
|
for tag in soup.findAll(name='a', attrs={'class':['deflink']}):
|
||||||
#,dict(name='a', attrs={'class':['edit']})
|
tag.replaceWith(tag.string)
|
||||||
,dict(name='p', attrs={'class':['byline']})
|
for tag in soup.findAll(name='a', text=re.compile(r'.*View Metric.*', re.DOTALL)):
|
||||||
]
|
tag.parent.parent.extract()
|
||||||
|
for tag in soup.findAll(name='a', text=re.compile(r'.*Add my own photo.*', re.DOTALL)):
|
||||||
|
tag.parent.parent.extract()
|
||||||
|
for tag in soup.findAll(name='div', attrs={'class':['container']}):
|
||||||
|
if tag.find(name='h1'):
|
||||||
|
continue
|
||||||
|
if tag.find(name='h2', text=re.compile(r'.*Ingredients.*', re.DOTALL)):
|
||||||
|
print 'tag found Ingred h2'
|
||||||
|
continue
|
||||||
|
if tag.find(name='h2', text=re.compile(r'Preparation.*', re.DOTALL)):
|
||||||
|
print 'tag found Prep h2'
|
||||||
|
continue
|
||||||
|
tag.extract()
|
||||||
|
return soup
|
||||||
|
|
||||||
feeds = [(u'4 & 5 Star Rated Recipes', u'http://feeds.feedburner.com/Bigovencom-RecipeRaves?format=xml')]
|
feeds = [(u'4 & 5 Star Rated Recipes', u'http://feeds.feedburner.com/Bigovencom-RecipeRaves?format=xml')]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
|
||||||
for tag in soup.findAll(name='a', attrs={'class':['edit']}):
|
|
||||||
tag.parent.extract()
|
|
||||||
for tag in soup.findAll(name='a', attrs={'class':['deflink']}):
|
|
||||||
tag.replaceWith(tag.string)
|
|
||||||
return soup
|
|
||||||
|
|
||||||
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:medium;}
|
|
||||||
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
|
||||||
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ from calibre import strftime
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class ElPaisImpresa(BasicNewsRecipe):
|
class ElPaisImpresa(BasicNewsRecipe):
|
||||||
title = 'El País - edicion impresa'
|
title = u'El Pa\xeds - edicion impresa'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic'
|
||||||
description = 'el periodico global en Español'
|
description = u'el periodico global en Espa\xf1ol'
|
||||||
publisher = 'EDICIONES EL PAIS, S.L.'
|
publisher = 'EDICIONES EL PAIS, S.L.'
|
||||||
category = 'news, politics,Spain,actualidad,noticias,informacion,videos,fotografias,audios,graficos,nacional,internacional,deportes,economia,tecnologia,cultura,gente,television,sociedad,opinion,blogs,foros,chats,encuestas,entrevistas,participacion'
|
category = 'news, politics,Spain,actualidad,noticias,informacion,videos,fotografias,audios,graficos,nacional,internacional,deportes,economia,tecnologia,cultura,gente,television,sociedad,opinion,blogs,foros,chats,encuestas,entrevistas,participacion'
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
@ -32,10 +32,10 @@ class ElPaisImpresa(BasicNewsRecipe):
|
|||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Internacional' , index + u'internacional/' )
|
(u'Internacional' , index + u'internacional/' )
|
||||||
,(u'España' , index + u'espana/' )
|
,(u'Espa\xf1a' , index + u'espana/' )
|
||||||
,(u'Economia' , index + u'economia/' )
|
,(u'Economia' , index + u'economia/' )
|
||||||
,(u'Opinion' , index + u'opinion/' )
|
,(u'Opinion' , index + u'opinion/' )
|
||||||
,(u'Viñetas' , index + u'vineta/' )
|
,(u'Vi\xf1etas' , index + u'vineta/' )
|
||||||
,(u'Sociedad' , index + u'sociedad/' )
|
,(u'Sociedad' , index + u'sociedad/' )
|
||||||
,(u'Cultura' , index + u'cultura/' )
|
,(u'Cultura' , index + u'cultura/' )
|
||||||
,(u'Tendencias' , index + u'tendencias/' )
|
,(u'Tendencias' , index + u'tendencias/' )
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
|
||||||
'''
|
'''
|
||||||
www.esquire.com
|
www.esquire.com
|
||||||
@ -9,7 +7,6 @@ www.esquire.com
|
|||||||
|
|
||||||
from calibre import strftime
|
from calibre import strftime
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.ebooks.BeautifulSoup import Tag
|
|
||||||
|
|
||||||
class Esquire(BasicNewsRecipe):
|
class Esquire(BasicNewsRecipe):
|
||||||
title = 'Esquire'
|
title = 'Esquire'
|
||||||
@ -23,21 +20,19 @@ class Esquire(BasicNewsRecipe):
|
|||||||
encoding = 'cp1250'
|
encoding = 'cp1250'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
publication_type = 'magazine'
|
||||||
lang = 'en-US'
|
masthead_url = 'http://www.esquire.com/cm/shared/site_images/print_this/esquire_logo.gif'
|
||||||
cover_url = strftime('http://www.esquire.com/cm/esquire/cover-images/%Y_') + strftime('%m').strip('0') + '.jpg'
|
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
, 'tags' : category
|
, 'tags' : category
|
||||||
, 'publisher' : publisher
|
, 'publisher' : publisher
|
||||||
, 'language' : lang
|
, 'language' : language
|
||||||
, 'pretty_print' : True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
|
keep_only_tags = [dict(name='div', attrs={'id':['article_header','article_content']})]
|
||||||
|
remove_tags = [dict(name=['object','link','embed','iframe','base'])]
|
||||||
remove_tags = [dict(name=['object','link','embed','iframe'])]
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Style' , u'http://www.esquire.com/style/rss/' )
|
(u'Style' , u'http://www.esquire.com/style/rss/' )
|
||||||
@ -47,17 +42,7 @@ class Esquire(BasicNewsRecipe):
|
|||||||
,(u'Frontpage', u'http://www.esquire.com/rss/' )
|
,(u'Frontpage', u'http://www.esquire.com/rss/' )
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def print_version(self, url):
|
|
||||||
rest = url.rpartition('?')[0]
|
|
||||||
article = rest.rpartition('/')[2]
|
|
||||||
return 'http://www.esquire.com/print-this/' + article
|
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
soup.html['xml:lang'] = self.lang
|
|
||||||
soup.html['lang'] = self.lang
|
|
||||||
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
|
|
||||||
soup.head.insert(0,mlang)
|
|
||||||
for item in soup.findAll(style=True):
|
for item in soup.findAll(style=True):
|
||||||
del item['style']
|
del item['style']
|
||||||
return soup
|
return soup
|
||||||
|
46
resources/recipes/fe_india.recipe
Normal file
46
resources/recipes/fe_india.recipe
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
financialexpress.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class FE_India(BasicNewsRecipe):
|
||||||
|
title = 'The Financial Express'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'Financial news from India'
|
||||||
|
publisher = 'The Indian Express Limited'
|
||||||
|
category = 'news, politics, finances, India'
|
||||||
|
oldest_article = 30
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'cp1252'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'en_IN'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
masthead_url = 'http://static.expressindia.com/frontend/fe/images/fe_logo.jpg'
|
||||||
|
publication_type = 'magazine'
|
||||||
|
extra_css = ' body{font-family: Arial,Helvetica,sans-serif } '
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [dict(attrs={'class':'txt'})]
|
||||||
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
|
feeds = [(u'Articles', u'http://www.expressindia.com/syndications/fe.xml')]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
article_raw = url.rpartition('/')[0]
|
||||||
|
article_id = article_raw.rpartition('/')[2]
|
||||||
|
return 'http://www.financialexpress.com/printer/news/' + article_id + '/'
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return soup
|
@ -1,13 +1,10 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
'''
|
'''
|
||||||
economictimes.indiatimes.com
|
economictimes.indiatimes.com
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.ebooks.BeautifulSoup import Tag
|
|
||||||
|
|
||||||
class TheEconomicTimes(BasicNewsRecipe):
|
class TheEconomicTimes(BasicNewsRecipe):
|
||||||
title = 'The Economic Times India'
|
title = 'The Economic Times India'
|
||||||
@ -21,18 +18,21 @@ class TheEconomicTimes(BasicNewsRecipe):
|
|||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
simultaneous_downloads = 1
|
simultaneous_downloads = 1
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
lang = 'en-IN'
|
|
||||||
language = 'en_IN'
|
language = 'en_IN'
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://economictimes.indiatimes.com/photo/2676871.cms'
|
||||||
|
extra_css = """ body{font-family: Arial,Helvetica,sans-serif}
|
||||||
|
.heading1{font-size: xx-large; font-weight: bold} """
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
html2lrf_options = [
|
keep_only_tags = [dict(attrs={'class':['heading1','headingnext','Normal']})]
|
||||||
'--comment', description
|
remove_tags = [dict(name=['object','link','embed','iframe','base','table','meta'])]
|
||||||
, '--category', category
|
|
||||||
, '--publisher', publisher
|
|
||||||
, '--ignore-tables'
|
|
||||||
]
|
|
||||||
|
|
||||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
|
||||||
|
|
||||||
feeds = [(u'All articles', u'http://economictimes.indiatimes.com/rssfeedsdefault.cms')]
|
feeds = [(u'All articles', u'http://economictimes.indiatimes.com/rssfeedsdefault.cms')]
|
||||||
|
|
||||||
@ -47,11 +47,6 @@ class TheEconomicTimes(BasicNewsRecipe):
|
|||||||
return rurl
|
return rurl
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
soup.html['xml:lang'] = self.lang
|
for item in soup.findAll(style=True):
|
||||||
soup.html['lang'] = self.lang
|
del item['style']
|
||||||
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
|
|
||||||
mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")])
|
|
||||||
soup.head.insert(0,mlang)
|
|
||||||
soup.head.insert(1,mcharset)
|
|
||||||
return self.adeify_images(soup)
|
return self.adeify_images(soup)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.7.13'
|
__version__ = '0.7.14'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -426,7 +426,7 @@ class KoboReaderOutput(OutputProfile):
|
|||||||
|
|
||||||
description = _('This profile is intended for the Kobo Reader.')
|
description = _('This profile is intended for the Kobo Reader.')
|
||||||
|
|
||||||
screen_size = (590, 775)
|
screen_size = (540, 718)
|
||||||
comic_screen_size = (540, 718)
|
comic_screen_size = (540, 718)
|
||||||
dpi = 168.451
|
dpi = 168.451
|
||||||
fbase = 12
|
fbase = 12
|
||||||
|
@ -33,7 +33,9 @@ class ANDROID(USBMS):
|
|||||||
|
|
||||||
# Samsung
|
# Samsung
|
||||||
0x04e8 : { 0x681d : [0x0222, 0x0400],
|
0x04e8 : { 0x681d : [0x0222, 0x0400],
|
||||||
0x681c : [0x0222, 0x0224, 0x0400]},
|
0x681c : [0x0222, 0x0224, 0x0400],
|
||||||
|
0x6640 : [0x0100],
|
||||||
|
},
|
||||||
|
|
||||||
# Acer
|
# Acer
|
||||||
0x502 : { 0x3203 : [0x0100]},
|
0x502 : { 0x3203 : [0x0100]},
|
||||||
@ -54,9 +56,9 @@ class ANDROID(USBMS):
|
|||||||
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX']
|
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX']
|
||||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959']
|
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID']
|
||||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959']
|
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID']
|
||||||
|
|
||||||
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
||||||
|
|
||||||
|
@ -56,6 +56,8 @@ class WinPNPScanner(object):
|
|||||||
def drive_order(self, pnp_id):
|
def drive_order(self, pnp_id):
|
||||||
order = 0
|
order = 0
|
||||||
match = re.search(r'REV_.*?&(\d+)#', pnp_id)
|
match = re.search(r'REV_.*?&(\d+)#', pnp_id)
|
||||||
|
if match is None:
|
||||||
|
match = re.search(r'REV_.*?&(\d+)', pnp_id)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
order = int(match.group(1))
|
order = int(match.group(1))
|
||||||
return order
|
return order
|
||||||
|
@ -20,20 +20,19 @@ class SNE(USBMS):
|
|||||||
|
|
||||||
# Ordered list of supported formats
|
# Ordered list of supported formats
|
||||||
# Be sure these have an entry in calibre.devices.mime
|
# Be sure these have an entry in calibre.devices.mime
|
||||||
FORMATS = ['epub', 'txt']
|
FORMATS = ['epub', 'pdf', 'txt']
|
||||||
|
|
||||||
VENDOR_ID = [0x04e8]
|
VENDOR_ID = [0x04e8]
|
||||||
PRODUCT_ID = [0x2051]
|
PRODUCT_ID = [0x2051, 0x2053]
|
||||||
BCD = [0x0323]
|
BCD = [0x0323]
|
||||||
|
|
||||||
VENDOR_NAME = ''
|
VENDOR_NAME = 'SAMSUNG'
|
||||||
#WINDOWS_MAIN_MEM = 'MASS_STORAGE'
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'SNE-60'
|
||||||
#WINDOWS_CARD_A_MEM = 'MASS_STORAGE'
|
|
||||||
|
|
||||||
MAIN_MEMORY_VOLUME_LABEL = 'SNE Main Memory'
|
MAIN_MEMORY_VOLUME_LABEL = 'SNE Main Memory'
|
||||||
STORAGE_CARD_VOLUME_LABEL = 'SNE Storage Card'
|
STORAGE_CARD_VOLUME_LABEL = 'SNE Storage Card'
|
||||||
|
|
||||||
EBOOK_DIR_MAIN = 'Book'
|
EBOOK_DIR_MAIN = 'Books'
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ from calibre.devices.usbms.driver import USBMS
|
|||||||
|
|
||||||
class TECLAST_K3(USBMS):
|
class TECLAST_K3(USBMS):
|
||||||
|
|
||||||
name = 'Teclast K3 Device Interface'
|
name = 'Teclast K3/K5 Device Interface'
|
||||||
gui_name = 'K3'
|
gui_name = 'K3/K5'
|
||||||
description = _('Communicate with the Teclast K3 reader.')
|
description = _('Communicate with the Teclast K3/K5 reader.')
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
supported_platforms = ['windows', 'osx', 'linux']
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
|
||||||
@ -17,11 +17,10 @@ class TECLAST_K3(USBMS):
|
|||||||
|
|
||||||
VENDOR_ID = [0x071b]
|
VENDOR_ID = [0x071b]
|
||||||
PRODUCT_ID = [0x3203]
|
PRODUCT_ID = [0x3203]
|
||||||
BCD = [0x0000]
|
BCD = [0x0000, 0x0100]
|
||||||
|
|
||||||
VENDOR_NAME = 'TECLAST'
|
VENDOR_NAME = 'TECLAST'
|
||||||
WINDOWS_MAIN_MEM = 'DIGITAL_PLAYER'
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['DIGITAL_PLAYER', 'TL-K5']
|
||||||
WINDOWS_CARD_A_MEM = 'DIGITAL_PLAYER'
|
|
||||||
|
|
||||||
MAIN_MEMORY_VOLUME_LABEL = 'K3 Main Memory'
|
MAIN_MEMORY_VOLUME_LABEL = 'K3 Main Memory'
|
||||||
STORAGE_CARD_VOLUME_LABEL = 'K3 Storage Card'
|
STORAGE_CARD_VOLUME_LABEL = 'K3 Storage Card'
|
||||||
|
@ -185,7 +185,7 @@ class Serializer(object):
|
|||||||
buffer.write('<guide>')
|
buffer.write('<guide>')
|
||||||
for ref in self.oeb.guide.values():
|
for ref in self.oeb.guide.values():
|
||||||
path = urldefrag(ref.href)[0]
|
path = urldefrag(ref.href)[0]
|
||||||
if hrefs[path].media_type not in OEB_DOCS:
|
if path not in hrefs or hrefs[path].media_type not in OEB_DOCS:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
buffer.write('<reference type="')
|
buffer.write('<reference type="')
|
||||||
|
@ -10,10 +10,10 @@ Module to implement the Cover Flow feature
|
|||||||
import sys, os, time
|
import sys, os, time
|
||||||
|
|
||||||
from PyQt4.Qt import QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, \
|
from PyQt4.Qt import QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, \
|
||||||
QStackedLayout, QLabel
|
QStackedLayout, QLabel, QByteArray, pyqtSignal
|
||||||
|
|
||||||
from calibre import plugins
|
from calibre import plugins
|
||||||
from calibre.gui2 import config, available_height, available_width
|
from calibre.gui2 import config, available_height, available_width, gprefs
|
||||||
|
|
||||||
pictureflow, pictureflowerror = plugins['pictureflow']
|
pictureflow, pictureflowerror = plugins['pictureflow']
|
||||||
|
|
||||||
@ -107,6 +107,28 @@ else:
|
|||||||
DatabaseImages = None
|
DatabaseImages = None
|
||||||
FileSystemImages = None
|
FileSystemImages = None
|
||||||
|
|
||||||
|
class CBDialog(QDialog):
|
||||||
|
|
||||||
|
closed = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent, cover_flow):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
self._layout = QStackedLayout()
|
||||||
|
self.setLayout(self._layout)
|
||||||
|
self.setWindowTitle(_('Browse by covers'))
|
||||||
|
self.layout().addWidget(cover_flow)
|
||||||
|
|
||||||
|
geom = gprefs.get('cover_browser_dialog_geometry', bytearray(''))
|
||||||
|
geom = QByteArray(geom)
|
||||||
|
if not self.restoreGeometry(geom):
|
||||||
|
h, w = available_height()-60, int(available_width()/1.5)
|
||||||
|
self.resize(w, h)
|
||||||
|
|
||||||
|
def closeEvent(self, *args):
|
||||||
|
geom = bytearray(self.saveGeometry())
|
||||||
|
gprefs['cover_browser_dialog_geometry'] = geom
|
||||||
|
self.closed.emit()
|
||||||
|
|
||||||
class CoverFlowMixin(object):
|
class CoverFlowMixin(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -129,6 +151,8 @@ class CoverFlowMixin(object):
|
|||||||
self.cover_flow.setWordWrap(True)
|
self.cover_flow.setWordWrap(True)
|
||||||
if config['separate_cover_flow']:
|
if config['separate_cover_flow']:
|
||||||
self.cb_splitter.button.clicked.connect(self.toggle_cover_browser)
|
self.cb_splitter.button.clicked.connect(self.toggle_cover_browser)
|
||||||
|
self.cb_splitter.button.set_state_to_show()
|
||||||
|
self.cb_splitter.action_toggle.triggered.connect(self.toggle_cover_browser)
|
||||||
if CoverFlow is not None:
|
if CoverFlow is not None:
|
||||||
self.cover_flow.stop.connect(self.hide_cover_browser)
|
self.cover_flow.stop.connect(self.hide_cover_browser)
|
||||||
else:
|
else:
|
||||||
@ -137,7 +161,7 @@ class CoverFlowMixin(object):
|
|||||||
self.cover_flow.stop.connect(self.cb_splitter.hide_side_pane)
|
self.cover_flow.stop.connect(self.cb_splitter.hide_side_pane)
|
||||||
self.cb_splitter.button.toggled.connect(self.cover_browser_toggled)
|
self.cb_splitter.button.toggled.connect(self.cover_browser_toggled)
|
||||||
|
|
||||||
def toggle_cover_browser(self):
|
def toggle_cover_browser(self, *args):
|
||||||
cbd = getattr(self, 'cb_dialog', None)
|
cbd = getattr(self, 'cb_dialog', None)
|
||||||
if cbd is not None:
|
if cbd is not None:
|
||||||
self.hide_cover_browser()
|
self.hide_cover_browser()
|
||||||
@ -171,25 +195,26 @@ class CoverFlowMixin(object):
|
|||||||
|
|
||||||
|
|
||||||
def show_cover_browser(self):
|
def show_cover_browser(self):
|
||||||
d = QDialog(self)
|
d = CBDialog(self, self.cover_flow)
|
||||||
ah, aw = available_height(), available_width()
|
|
||||||
d.resize(int(aw/1.5), ah-60)
|
|
||||||
d._layout = QStackedLayout()
|
|
||||||
d.setLayout(d._layout)
|
|
||||||
d.setWindowTitle(_('Browse by covers'))
|
|
||||||
d.layout().addWidget(self.cover_flow)
|
|
||||||
self.cover_flow.setVisible(True)
|
self.cover_flow.setVisible(True)
|
||||||
self.cover_flow.setFocus(Qt.OtherFocusReason)
|
self.cover_flow.setFocus(Qt.OtherFocusReason)
|
||||||
d.show()
|
d.show()
|
||||||
self.cb_splitter.button.set_state_to_hide()
|
self.cb_splitter.button.set_state_to_hide()
|
||||||
d.finished.connect(self.cb_splitter.button.set_state_to_show)
|
d.closed.connect(self.cover_browser_closed)
|
||||||
self.cb_dialog = d
|
self.cb_dialog = d
|
||||||
|
self.cb_splitter.button.set_state_to_hide()
|
||||||
|
|
||||||
def hide_cover_browser(self):
|
def cover_browser_closed(self, *args):
|
||||||
|
self.cb_dialog = None
|
||||||
|
self.cb_splitter.button.set_state_to_show()
|
||||||
|
|
||||||
|
def hide_cover_browser(self, *args):
|
||||||
cbd = getattr(self, 'cb_dialog', None)
|
cbd = getattr(self, 'cb_dialog', None)
|
||||||
if cbd is not None:
|
if cbd is not None:
|
||||||
cbd.accept()
|
cbd.accept()
|
||||||
self.cb_dialog = None
|
self.cb_dialog = None
|
||||||
|
self.cb_splitter.button.set_state_to_show()
|
||||||
|
|
||||||
|
|
||||||
def sync_cf_to_listview(self, current, previous):
|
def sync_cf_to_listview(self, current, previous):
|
||||||
if self.cover_flow_sync_flag and self.cover_flow.isVisible() and \
|
if self.cover_flow_sync_flag and self.cover_flow.isVisible() and \
|
||||||
|
@ -515,15 +515,15 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
|||||||
self.reset_confirmation_button.clicked.connect(self.reset_confirmation)
|
self.reset_confirmation_button.clicked.connect(self.reset_confirmation)
|
||||||
|
|
||||||
deft, curt = read_raw_tweaks()
|
deft, curt = read_raw_tweaks()
|
||||||
self.current_tweaks.setPlainText(curt)
|
self.current_tweaks.setPlainText(curt.decode('utf-8'))
|
||||||
self.default_tweaks.setPlainText(deft)
|
self.default_tweaks.setPlainText(deft.decode('utf-8'))
|
||||||
self.restore_tweaks_to_default_button.clicked.connect(self.restore_tweaks_to_default)
|
self.restore_tweaks_to_default_button.clicked.connect(self.restore_tweaks_to_default)
|
||||||
|
|
||||||
self.category_view.setCurrentIndex(self.category_view.model().index_for_name(initial_category))
|
self.category_view.setCurrentIndex(self.category_view.model().index_for_name(initial_category))
|
||||||
|
|
||||||
def restore_tweaks_to_default(self, *args):
|
def restore_tweaks_to_default(self, *args):
|
||||||
deft, curt = read_raw_tweaks()
|
deft, curt = read_raw_tweaks()
|
||||||
self.current_tweaks.setPlainText(deft)
|
self.current_tweaks.setPlainText(deft.decode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
def reset_confirmation(self):
|
def reset_confirmation(self):
|
||||||
@ -698,8 +698,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
|||||||
self.input_order.setCurrentRow(idx-1)
|
self.input_order.setCurrentRow(idx-1)
|
||||||
|
|
||||||
def set_tweaks(self):
|
def set_tweaks(self):
|
||||||
raw = unicode(self.current_tweaks.toPlainText())
|
raw = unicode(self.current_tweaks.toPlainText()).encode('utf-8')
|
||||||
raw = re.sub(r'(?m)^#.*fileencoding.*', '# ', raw)
|
|
||||||
try:
|
try:
|
||||||
exec raw
|
exec raw
|
||||||
except:
|
except:
|
||||||
|
@ -100,11 +100,11 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
|
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
col = unicode(self.column_name_box.text()).lower()
|
col = unicode(self.column_name_box.text())
|
||||||
if not col:
|
if not col:
|
||||||
return self.simple_error('', _('No lookup name was provided'))
|
return self.simple_error('', _('No lookup name was provided'))
|
||||||
if re.match('^\w*$', col) is None or not col[0].isalpha():
|
if re.match('^\w*$', col) is None or not col[0].isalpha() or col.lower() != col:
|
||||||
return self.simple_error('', _('The label must contain only letters, digits and underscores, and start with a letter'))
|
return self.simple_error('', _('The lookup name must contain only lower case letters, digits and underscores, and start with a letter'))
|
||||||
col_heading = unicode(self.column_heading_box.text())
|
col_heading = unicode(self.column_heading_box.text())
|
||||||
col_type = self.column_types[self.column_type_box.currentIndex()]['datatype']
|
col_type = self.column_types[self.column_type_box.currentIndex()]['datatype']
|
||||||
if col_type == '*text':
|
if col_type == '*text':
|
||||||
@ -130,8 +130,6 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
bad_head = True
|
bad_head = True
|
||||||
if bad_head:
|
if bad_head:
|
||||||
return self.simple_error('', _('The heading %s is already used')%col_heading)
|
return self.simple_error('', _('The heading %s is already used')%col_heading)
|
||||||
if ':' in col or ' ' in col or col.lower() != col:
|
|
||||||
return self.simple_error('', _('The lookup name must be lower case and cannot contain ":"s or spaces'))
|
|
||||||
|
|
||||||
date_format = {}
|
date_format = {}
|
||||||
if col_type == 'datetime':
|
if col_type == 'datetime':
|
||||||
|
@ -166,7 +166,8 @@ class LibraryWidget(Splitter): # {{{
|
|||||||
I('cover_flow.svg'),
|
I('cover_flow.svg'),
|
||||||
orientation=orientation, parent=parent,
|
orientation=orientation, parent=parent,
|
||||||
connect_button=not config['separate_cover_flow'],
|
connect_button=not config['separate_cover_flow'],
|
||||||
side_index=idx, initial_side_size=size, initial_show=False)
|
side_index=idx, initial_side_size=size, initial_show=False,
|
||||||
|
shortcut=_('Shift+Alt+B'))
|
||||||
parent.library_view = BooksView(parent)
|
parent.library_view = BooksView(parent)
|
||||||
parent.library_view.setObjectName('library_view')
|
parent.library_view.setObjectName('library_view')
|
||||||
self.addWidget(parent.library_view)
|
self.addWidget(parent.library_view)
|
||||||
@ -181,7 +182,8 @@ class Stack(QStackedWidget): # {{{
|
|||||||
self.tb_widget = TagBrowserWidget(parent)
|
self.tb_widget = TagBrowserWidget(parent)
|
||||||
parent.tb_splitter = Splitter('tag_browser_splitter',
|
parent.tb_splitter = Splitter('tag_browser_splitter',
|
||||||
_('Tag Browser'), I('tags.svg'),
|
_('Tag Browser'), I('tags.svg'),
|
||||||
parent=parent, side_index=0, initial_side_size=200)
|
parent=parent, side_index=0, initial_side_size=200,
|
||||||
|
shortcut=_('Shift+Alt+T'))
|
||||||
parent.tb_splitter.addWidget(self.tb_widget)
|
parent.tb_splitter.addWidget(self.tb_widget)
|
||||||
parent.tb_splitter.addWidget(parent.cb_splitter)
|
parent.tb_splitter.addWidget(parent.cb_splitter)
|
||||||
parent.tb_splitter.setCollapsible(parent.tb_splitter.other_index, False)
|
parent.tb_splitter.setCollapsible(parent.tb_splitter.other_index, False)
|
||||||
@ -274,7 +276,8 @@ class LayoutMixin(object): # {{{
|
|||||||
self.stack = Stack(self)
|
self.stack = Stack(self)
|
||||||
self.bd_splitter = Splitter('book_details_splitter',
|
self.bd_splitter = Splitter('book_details_splitter',
|
||||||
_('Book Details'), I('book.svg'),
|
_('Book Details'), I('book.svg'),
|
||||||
orientation=Qt.Vertical, parent=self, side_index=1)
|
orientation=Qt.Vertical, parent=self, side_index=1,
|
||||||
|
shortcut=_('Alt+D'))
|
||||||
self.bd_splitter.addWidget(self.stack)
|
self.bd_splitter.addWidget(self.stack)
|
||||||
self.bd_splitter.addWidget(self.book_details)
|
self.bd_splitter.addWidget(self.book_details)
|
||||||
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
|
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
|
||||||
@ -283,7 +286,8 @@ class LayoutMixin(object): # {{{
|
|||||||
else: # wide {{{
|
else: # wide {{{
|
||||||
self.bd_splitter = Splitter('book_details_splitter',
|
self.bd_splitter = Splitter('book_details_splitter',
|
||||||
_('Book Details'), I('book.svg'), initial_side_size=200,
|
_('Book Details'), I('book.svg'), initial_side_size=200,
|
||||||
orientation=Qt.Horizontal, parent=self, side_index=1)
|
orientation=Qt.Horizontal, parent=self, side_index=1,
|
||||||
|
shortcut=_('Shift+Alt+D'))
|
||||||
self.stack = Stack(self)
|
self.stack = Stack(self)
|
||||||
self.bd_splitter.addWidget(self.stack)
|
self.bd_splitter.addWidget(self.stack)
|
||||||
self.book_details = BookDetails(True, self)
|
self.book_details = BookDetails(True, self)
|
||||||
|
@ -166,6 +166,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
def __init__(self, pathtoebook=None, debug_javascript=False):
|
def __init__(self, pathtoebook=None, debug_javascript=False):
|
||||||
MainWindow.__init__(self, None)
|
MainWindow.__init__(self, None)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
self.base_window_title = unicode(self.windowTitle())
|
||||||
self.iterator = None
|
self.iterator = None
|
||||||
self.current_page = None
|
self.current_page = None
|
||||||
self.pending_search = None
|
self.pending_search = None
|
||||||
@ -602,7 +603,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.toc_model = TOC(self.iterator.toc)
|
self.toc_model = TOC(self.iterator.toc)
|
||||||
self.toc.setModel(self.toc_model)
|
self.toc.setModel(self.toc_model)
|
||||||
self.current_title = title
|
self.current_title = title
|
||||||
self.setWindowTitle(unicode(self.windowTitle())+' - '+title)
|
self.setWindowTitle(self.base_window_title+' - '+title)
|
||||||
self.pos.setMaximum(sum(self.iterator.pages))
|
self.pos.setMaximum(sum(self.iterator.pages))
|
||||||
self.pos.setSuffix(' / %d'%sum(self.iterator.pages))
|
self.pos.setSuffix(' / %d'%sum(self.iterator.pages))
|
||||||
self.vertical_scrollbar.setMinimum(100)
|
self.vertical_scrollbar.setMinimum(100)
|
||||||
|
@ -5,7 +5,7 @@ Miscellaneous widgets used in the GUI
|
|||||||
'''
|
'''
|
||||||
import re, os, traceback
|
import re, os, traceback
|
||||||
|
|
||||||
from PyQt4.Qt import QIcon, QFont, QLabel, QListWidget, \
|
from PyQt4.Qt import QIcon, QFont, QLabel, QListWidget, QAction, \
|
||||||
QListWidgetItem, QTextCharFormat, QApplication, \
|
QListWidgetItem, QTextCharFormat, QApplication, \
|
||||||
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
||||||
QPixmap, QSplitterHandle, QToolButton, \
|
QPixmap, QSplitterHandle, QToolButton, \
|
||||||
@ -855,7 +855,7 @@ class SplitterHandle(QSplitterHandle):
|
|||||||
|
|
||||||
class LayoutButton(QToolButton):
|
class LayoutButton(QToolButton):
|
||||||
|
|
||||||
def __init__(self, icon, text, splitter, parent=None):
|
def __init__(self, icon, text, splitter, parent=None, shortcut=None):
|
||||||
QToolButton.__init__(self, parent)
|
QToolButton.__init__(self, parent)
|
||||||
self.label = text
|
self.label = text
|
||||||
self.setIcon(QIcon(icon))
|
self.setIcon(QIcon(icon))
|
||||||
@ -864,18 +864,21 @@ class LayoutButton(QToolButton):
|
|||||||
self.splitter = splitter
|
self.splitter = splitter
|
||||||
splitter.state_changed.connect(self.update_state)
|
splitter.state_changed.connect(self.update_state)
|
||||||
self.setCursor(Qt.PointingHandCursor)
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
|
self.shortcut = ''
|
||||||
|
if shortcut:
|
||||||
|
self.shortcut = shortcut
|
||||||
|
|
||||||
def set_state_to_show(self, *args):
|
def set_state_to_show(self, *args):
|
||||||
self.setChecked(False)
|
self.setChecked(False)
|
||||||
label =_('Show')
|
label =_('Show')
|
||||||
self.setText(label + ' ' + self.label)
|
self.setText(label + ' ' + self.label + ' ' + self.shortcut)
|
||||||
self.setToolTip(self.text())
|
self.setToolTip(self.text())
|
||||||
self.setStatusTip(self.text())
|
self.setStatusTip(self.text())
|
||||||
|
|
||||||
def set_state_to_hide(self, *args):
|
def set_state_to_hide(self, *args):
|
||||||
self.setChecked(True)
|
self.setChecked(True)
|
||||||
label = _('Hide')
|
label = _('Hide')
|
||||||
self.setText(label + ' ' + self.label)
|
self.setText(label + ' ' + self.label+ ' ' + self.shortcut)
|
||||||
self.setToolTip(self.text())
|
self.setToolTip(self.text())
|
||||||
self.setStatusTip(self.text())
|
self.setStatusTip(self.text())
|
||||||
|
|
||||||
@ -891,7 +894,7 @@ class Splitter(QSplitter):
|
|||||||
|
|
||||||
def __init__(self, name, label, icon, initial_show=True,
|
def __init__(self, name, label, icon, initial_show=True,
|
||||||
initial_side_size=120, connect_button=True,
|
initial_side_size=120, connect_button=True,
|
||||||
orientation=Qt.Horizontal, side_index=0, parent=None):
|
orientation=Qt.Horizontal, side_index=0, parent=None, shortcut=None):
|
||||||
QSplitter.__init__(self, parent)
|
QSplitter.__init__(self, parent)
|
||||||
self.resize_timer = QTimer(self)
|
self.resize_timer = QTimer(self)
|
||||||
self.resize_timer.setSingleShot(True)
|
self.resize_timer.setSingleShot(True)
|
||||||
@ -906,10 +909,21 @@ class Splitter(QSplitter):
|
|||||||
self.initial_side_size = initial_side_size
|
self.initial_side_size = initial_side_size
|
||||||
self.initial_show = initial_show
|
self.initial_show = initial_show
|
||||||
self.splitterMoved.connect(self.splitter_moved, type=Qt.QueuedConnection)
|
self.splitterMoved.connect(self.splitter_moved, type=Qt.QueuedConnection)
|
||||||
self.button = LayoutButton(icon, label, self)
|
self.button = LayoutButton(icon, label, self, shortcut=shortcut)
|
||||||
if connect_button:
|
if connect_button:
|
||||||
self.button.clicked.connect(self.double_clicked)
|
self.button.clicked.connect(self.double_clicked)
|
||||||
|
|
||||||
|
if shortcut is not None:
|
||||||
|
self.action_toggle = QAction(QIcon(icon), _('Toggle') + ' ' + label,
|
||||||
|
self)
|
||||||
|
self.action_toggle.triggered.connect(self.toggle_triggered)
|
||||||
|
self.action_toggle.setShortcut(shortcut)
|
||||||
|
if parent is not None:
|
||||||
|
parent.addAction(self.action_toggle)
|
||||||
|
|
||||||
|
def toggle_triggered(self, *args):
|
||||||
|
self.toggle_side_pane()
|
||||||
|
|
||||||
def createHandle(self):
|
def createHandle(self):
|
||||||
return SplitterHandle(self.orientation(), self)
|
return SplitterHandle(self.orientation(), self)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import json
|
import json, re
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from math import floor
|
from math import floor
|
||||||
|
|
||||||
@ -430,6 +430,10 @@ class CustomColumns(object):
|
|||||||
|
|
||||||
def create_custom_column(self, label, name, datatype, is_multiple,
|
def create_custom_column(self, label, name, datatype, is_multiple,
|
||||||
editable=True, display={}):
|
editable=True, display={}):
|
||||||
|
if not label:
|
||||||
|
raise ValueError(_('No label was provided'))
|
||||||
|
if re.match('^\w*$', label) is None or not label[0].isalpha() or label.lower() != label:
|
||||||
|
raise ValueError(_('The label must contain only lower case letters, digits and underscores, and start with a letter'))
|
||||||
if datatype not in self.CUSTOM_DATA_TYPES:
|
if datatype not in self.CUSTOM_DATA_TYPES:
|
||||||
raise ValueError('%r is not a supported data type'%datatype)
|
raise ValueError('%r is not a supported data type'%datatype)
|
||||||
normalized = datatype not in ('datetime', 'comments', 'int', 'bool',
|
normalized = datatype not in ('datetime', 'comments', 'int', 'bool',
|
||||||
|
@ -433,6 +433,28 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
parent = os.path.dirname(spath)
|
parent = os.path.dirname(spath)
|
||||||
if len(os.listdir(parent)) == 0:
|
if len(os.listdir(parent)) == 0:
|
||||||
self.rmtree(parent, permanent=True)
|
self.rmtree(parent, permanent=True)
|
||||||
|
curpath = self.library_path
|
||||||
|
c1, c2 = current_path.split('/'), path.split('/')
|
||||||
|
if not self.is_case_sensitive and len(c1) == len(c2):
|
||||||
|
# On case-insensitive systems, title and author renames that only
|
||||||
|
# change case don't cause any changes to the directories in the file
|
||||||
|
# system. This can lead to having the directory names not match the
|
||||||
|
# title/author, which leads to trouble when libraries are copied to
|
||||||
|
# a case-sensitive system. The following code fixes this by checking
|
||||||
|
# each segment. If they are different because of case, then rename
|
||||||
|
# the segment to some temp file name, then rename it back to the
|
||||||
|
# correct name. Note that the code above correctly handles files in
|
||||||
|
# the directories, so no need to do them here.
|
||||||
|
for oldseg, newseg in zip(c1, c2):
|
||||||
|
if oldseg.lower() == newseg.lower() and oldseg != newseg:
|
||||||
|
while True:
|
||||||
|
# need a temp name in the current segment for renames
|
||||||
|
tempname = os.path.join(curpath, 'TEMP.%f'%time.time())
|
||||||
|
if not os.path.exists(tempname):
|
||||||
|
break
|
||||||
|
os.rename(os.path.join(curpath, oldseg), tempname)
|
||||||
|
os.rename(tempname, os.path.join(curpath, newseg))
|
||||||
|
curpath = os.path.join(curpath, newseg)
|
||||||
|
|
||||||
def add_listener(self, listener):
|
def add_listener(self, listener):
|
||||||
'''
|
'''
|
||||||
|
@ -30,8 +30,8 @@ Environment variables
|
|||||||
Tweaks
|
Tweaks
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Tweaks are small changes that you can specify to control various aspects of |app|'s behavior. You specify them by editing the 2tweaks.py file in the config directory.
|
Tweaks are small changes that you can specify to control various aspects of |app|'s behavior. You can change them by going to Preferences->Advanced->Tweaks.
|
||||||
The default tweaks.py file is reproduced below
|
The default values for the tweaks are reproduced below
|
||||||
|
|
||||||
.. literalinclude:: ../../../resources/default_tweaks.py
|
.. literalinclude:: ../../../resources/default_tweaks.py
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user