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
14bdeba5e3
63
resources/recipes/auto.recipe
Normal file
63
resources/recipes/auto.recipe
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'GabrieleMarini, based on Darko Miletic'
|
||||||
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>, Gabriele Marini'
|
||||||
|
__version__ = 'v1.02 Marini Gabriele '
|
||||||
|
__date__ = '14062010'
|
||||||
|
__description__ = 'Italian daily newspaper'
|
||||||
|
|
||||||
|
'''
|
||||||
|
http://www.corrieredellosport.it/
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Auto(BasicNewsRecipe):
|
||||||
|
__author__ = 'Gabriele Marini'
|
||||||
|
description = 'Auto and Formula 1'
|
||||||
|
|
||||||
|
cover_url = 'http://www.auto.it/res/imgs/logo_Auto.png'
|
||||||
|
|
||||||
|
|
||||||
|
title = u'Auto'
|
||||||
|
publisher = 'CONTE Editore'
|
||||||
|
category = 'Sport'
|
||||||
|
|
||||||
|
language = 'it'
|
||||||
|
timefmt = '[%a, %d %b, %Y]'
|
||||||
|
|
||||||
|
oldest_article = 60
|
||||||
|
max_articles_per_feed = 30
|
||||||
|
use_embedded_content = False
|
||||||
|
recursion = 10
|
||||||
|
|
||||||
|
remove_javascript = True
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
|
html2lrf_options = [
|
||||||
|
'--comment', description
|
||||||
|
, '--category', category
|
||||||
|
, '--publisher', publisher
|
||||||
|
, '--ignore-tables'
|
||||||
|
]
|
||||||
|
|
||||||
|
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='h2', attrs={'class':['tit_Article y_Txt']}),
|
||||||
|
dict(name='h2', attrs={'class':['tit_Article']}),
|
||||||
|
dict(name='div', attrs={'class':['box_Img newsdet_new ']}),
|
||||||
|
dict(name='div', attrs={'class':['box_Img newsdet_as ']}),
|
||||||
|
dict(name='table', attrs={'class':['table_A']}),
|
||||||
|
dict(name='div', attrs={'class':['txt_Article txtBox_cms']}),
|
||||||
|
dict(name='testoscheda')]
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Tutte le News' , u'http://www.auto.it/rss/articoli.xml' ),
|
||||||
|
(u'Prove su Strada' , u'http://www.auto.it/rss/prove+6.xml'),
|
||||||
|
(u'Novit\xe0' , u'http://www.auto.it/rss/novita+3.xml')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
60
resources/recipes/corriere_dello_sport.recipe
Normal file
60
resources/recipes/corriere_dello_sport.recipe
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'GabrieleMarini, based on Darko Miletic'
|
||||||
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>, Gabriele Marini'
|
||||||
|
__version__ = ' '
|
||||||
|
__date__ = '14-06-2010'
|
||||||
|
__description__ = 'Italian daily newspaper'
|
||||||
|
|
||||||
|
'''
|
||||||
|
http://www.corrieredellosport.it/
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class ilCorrieredelloSport(BasicNewsRecipe):
|
||||||
|
__author__ = 'Gabriele Marini'
|
||||||
|
description = 'Italian daily newspaper'
|
||||||
|
|
||||||
|
cover_url = 'http://edicola.corrieredellosport.it/newsmem/corsport/prima/nazionale_prima.jpg'
|
||||||
|
|
||||||
|
|
||||||
|
title = u'Il Corriere dello Sport'
|
||||||
|
publisher = 'CORRIERE DELLO SPORT s.r.l. '
|
||||||
|
category = 'Sport'
|
||||||
|
|
||||||
|
language = 'it'
|
||||||
|
timefmt = '[%a, %d %b, %Y]'
|
||||||
|
|
||||||
|
oldest_article = 10
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
use_embedded_content = False
|
||||||
|
recursion = 10
|
||||||
|
|
||||||
|
remove_javascript = True
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
|
html2lrf_options = [
|
||||||
|
'--comment', description
|
||||||
|
, '--category', category
|
||||||
|
, '--publisher', publisher
|
||||||
|
, '--ignore-tables'
|
||||||
|
]
|
||||||
|
|
||||||
|
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='h1', attrs={'class':['tit_Article']}),
|
||||||
|
dict(name='h1', attrs={'class':['tit_Article_mondiali']}),
|
||||||
|
dict(name='div', attrs={'class':['box_Img']}),
|
||||||
|
dict(name='p', attrs={'class':['summary','text']})]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Primo Piano' , u'http://www.corrieredellosport.it/rss/primo_piano.xml' ),
|
||||||
|
(u'Calcio' , u'http://www.corrieredellosport.it/rss/Calcio-3.xml'),
|
||||||
|
(u'Formula 1' , u'http://www.corrieredellosport.it/rss/Formula-1-7.xml'),
|
||||||
|
(u'Moto' , u'http://www.corrieredellosport.it/rss/Moto-8.xml'),
|
||||||
|
(u'Piu visti' , u'http://www.corrieredellosport.it/rss/piu_visti.xml')
|
||||||
|
]
|
@ -7,12 +7,33 @@ __docformat__ = 'restructuredtext en'
|
|||||||
'''
|
'''
|
||||||
Device driver for Amazon's Kindle
|
Device driver for Amazon's Kindle
|
||||||
'''
|
'''
|
||||||
import datetime, os, re, sys
|
import datetime, os, re, sys, json, hashlib
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from struct import unpack
|
from struct import unpack
|
||||||
|
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
|
'''
|
||||||
|
Notes on collections:
|
||||||
|
|
||||||
|
A collections cache is stored at system/collections.json
|
||||||
|
The cache is read only, changes made to it are overwritten (it is regenerated)
|
||||||
|
on device disconnect
|
||||||
|
|
||||||
|
A log of collection creation/manipulation is available at
|
||||||
|
system/userannotationlog
|
||||||
|
|
||||||
|
collections.json refers to books via a SHA1 hash of the absolute path to the
|
||||||
|
book (prefix is /mnt/us on my Kindle). The SHA1 hash may or may not be prefixed
|
||||||
|
by some characters, use the last 40 characters.
|
||||||
|
|
||||||
|
Changing the metadata and resending the file doesn't seem to affect collections
|
||||||
|
|
||||||
|
Adding a book to a collection on the Kindle does not change the book file at all
|
||||||
|
(i.e. it is binary identical). Therefore collection information is not stored in
|
||||||
|
file metadata.
|
||||||
|
'''
|
||||||
|
|
||||||
class KINDLE(USBMS):
|
class KINDLE(USBMS):
|
||||||
|
|
||||||
name = 'Kindle Device Interface'
|
name = 'Kindle Device Interface'
|
||||||
@ -60,6 +81,7 @@ class KINDLE(USBMS):
|
|||||||
'replace')
|
'replace')
|
||||||
return mi
|
return mi
|
||||||
|
|
||||||
|
|
||||||
def get_annotations(self, path_map):
|
def get_annotations(self, path_map):
|
||||||
MBP_FORMATS = [u'azw', u'mobi', u'prc', u'txt']
|
MBP_FORMATS = [u'azw', u'mobi', u'prc', u'txt']
|
||||||
mbp_formats = set(MBP_FORMATS)
|
mbp_formats = set(MBP_FORMATS)
|
||||||
@ -150,6 +172,37 @@ class KINDLE2(KINDLE):
|
|||||||
PRODUCT_ID = [0x0002]
|
PRODUCT_ID = [0x0002]
|
||||||
BCD = [0x0100]
|
BCD = [0x0100]
|
||||||
|
|
||||||
|
def books(self, oncard=None, end_session=True):
|
||||||
|
bl = USBMS.books(self, oncard=oncard, end_session=end_session)
|
||||||
|
# Read collections information
|
||||||
|
collections = os.path.join(self._main_prefix, 'system', 'collections.json')
|
||||||
|
if os.access(collections, os.R_OK):
|
||||||
|
try:
|
||||||
|
self.kindle_update_booklist(bl, collections)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return bl
|
||||||
|
|
||||||
|
def kindle_update_booklist(self, bl, collections):
|
||||||
|
with open(collections, 'rb') as f:
|
||||||
|
collections = f.read()
|
||||||
|
collections = json.loads(collections)
|
||||||
|
path_map = {}
|
||||||
|
for name, val in collections.items():
|
||||||
|
col = name.split('@')[0]
|
||||||
|
items = val.get('items', [])
|
||||||
|
for x in items:
|
||||||
|
x = x[-40:]
|
||||||
|
if x not in path_map:
|
||||||
|
path_map[x] = set([])
|
||||||
|
path_map[x].add(col)
|
||||||
|
if path_map:
|
||||||
|
for book in bl:
|
||||||
|
path = '/mnt/us/'+book.lpath
|
||||||
|
h = hashlib.sha1(path).hexdigest()
|
||||||
|
if h in path_map:
|
||||||
|
book.device_collections = list(sorted(path_map[h]))
|
||||||
|
|
||||||
class KINDLE_DX(KINDLE2):
|
class KINDLE_DX(KINDLE2):
|
||||||
|
|
||||||
|
@ -10,12 +10,31 @@ import os
|
|||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
|
||||||
from calibre.customize import FileTypePlugin
|
from calibre.customize import FileTypePlugin
|
||||||
|
from calibre.utils.zipfile import ZipFile, stringFileHeader
|
||||||
|
|
||||||
def is_comic(list_of_names):
|
def is_comic(list_of_names):
|
||||||
extensions = set([x.rpartition('.')[-1].lower() for x in list_of_names])
|
extensions = set([x.rpartition('.')[-1].lower() for x in list_of_names])
|
||||||
comic_extensions = set(['jpg', 'jpeg', 'png'])
|
comic_extensions = set(['jpg', 'jpeg', 'png'])
|
||||||
return len(extensions - comic_extensions) == 0
|
return len(extensions - comic_extensions) == 0
|
||||||
|
|
||||||
|
def archive_type(stream):
|
||||||
|
try:
|
||||||
|
pos = stream.tell()
|
||||||
|
except:
|
||||||
|
pos = 0
|
||||||
|
id_ = stream.read(4)
|
||||||
|
ans = None
|
||||||
|
if id_ == stringFileHeader:
|
||||||
|
ans = 'zip'
|
||||||
|
elif id_.startswith('Rar'):
|
||||||
|
ans = 'rar'
|
||||||
|
try:
|
||||||
|
stream.seek(pos)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
class ArchiveExtract(FileTypePlugin):
|
class ArchiveExtract(FileTypePlugin):
|
||||||
name = 'Archive Extract'
|
name = 'Archive Extract'
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
@ -31,7 +50,6 @@ class ArchiveExtract(FileTypePlugin):
|
|||||||
if is_rar:
|
if is_rar:
|
||||||
from calibre.libunrar import extract_member, names
|
from calibre.libunrar import extract_member, names
|
||||||
else:
|
else:
|
||||||
from calibre.utils.zipfile import ZipFile
|
|
||||||
zf = ZipFile(archive, 'r')
|
zf = ZipFile(archive, 'r')
|
||||||
|
|
||||||
if is_rar:
|
if is_rar:
|
||||||
|
@ -100,7 +100,7 @@ def _config():
|
|||||||
c.add_opt('tag_browser_hidden_categories', default=set(),
|
c.add_opt('tag_browser_hidden_categories', default=set(),
|
||||||
help=_('tag browser categories not to display'))
|
help=_('tag browser categories not to display'))
|
||||||
c.add_opt('gui_layout', choices=['wide', 'narrow'],
|
c.add_opt('gui_layout', choices=['wide', 'narrow'],
|
||||||
help=_('The layout of the user interface'), default='narrow')
|
help=_('The layout of the user interface'), default='wide')
|
||||||
return ConfigProxy(c)
|
return ConfigProxy(c)
|
||||||
|
|
||||||
config = _config()
|
config = _config()
|
||||||
|
@ -8,73 +8,18 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import os, collections
|
import os, collections
|
||||||
|
|
||||||
from PyQt4.Qt import QLabel, QPixmap, QSize, QWidget, Qt, pyqtSignal, \
|
from PyQt4.Qt import QLabel, QPixmap, QSize, QWidget, Qt, pyqtSignal, \
|
||||||
QVBoxLayout, QScrollArea
|
QVBoxLayout, QScrollArea, QPropertyAnimation, QEasingCurve, \
|
||||||
|
QSizePolicy, QPainter, QRect, pyqtProperty, QDesktopServices, QUrl
|
||||||
|
|
||||||
from calibre import fit_image, prepare_string_for_xml
|
from calibre import fit_image, prepare_string_for_xml
|
||||||
from calibre.gui2.widgets import IMAGE_EXTENSIONS
|
from calibre.gui2.widgets import IMAGE_EXTENSIONS
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
from calibre.constants import preferred_encoding
|
from calibre.constants import preferred_encoding
|
||||||
|
from calibre.library.comments import comments_to_html
|
||||||
|
|
||||||
class CoverView(QLabel):
|
# render_rows(data) {{{
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
QLabel.__init__(self, parent)
|
|
||||||
self.default_pixmap = QPixmap(I('book.svg'))
|
|
||||||
self.max_width, self.max_height = 120, 120
|
|
||||||
self.setScaledContents(True)
|
|
||||||
self.setPixmap(self.default_pixmap)
|
|
||||||
|
|
||||||
def do_layout(self):
|
|
||||||
pixmap = self.pixmap()
|
|
||||||
pwidth, pheight = pixmap.width(), pixmap.height()
|
|
||||||
width, height = fit_image(pwidth, pheight,
|
|
||||||
self.max_width, self.max_height)[1:]
|
|
||||||
self.setMaximumWidth(width)
|
|
||||||
try:
|
|
||||||
aspect_ratio = pwidth/float(pheight)
|
|
||||||
except ZeroDivisionError:
|
|
||||||
aspect_ratio = 1
|
|
||||||
mh = min(self.max_height, int(width/aspect_ratio))
|
|
||||||
self.setMaximumHeight(mh)
|
|
||||||
|
|
||||||
def setPixmap(self, pixmap):
|
|
||||||
QLabel.setPixmap(self, pixmap)
|
|
||||||
self.do_layout()
|
|
||||||
|
|
||||||
|
|
||||||
def sizeHint(self):
|
|
||||||
return QSize(self.maximumWidth(), self.maximumHeight())
|
|
||||||
|
|
||||||
def relayout(self, parent_size):
|
|
||||||
self.max_height = int(parent_size.height()/3.)
|
|
||||||
self.max_width = parent_size.width()
|
|
||||||
self.do_layout()
|
|
||||||
|
|
||||||
def show_data(self, data):
|
|
||||||
if data.has_key('cover'):
|
|
||||||
self.setPixmap(QPixmap.fromImage(data.pop('cover')))
|
|
||||||
else:
|
|
||||||
self.setPixmap(self.default_pixmap)
|
|
||||||
|
|
||||||
class BookInfo(QScrollArea):
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
QScrollArea.__init__(self, parent)
|
|
||||||
self.setWidgetResizable(True)
|
|
||||||
self.label = QLabel()
|
|
||||||
self.label.setWordWrap(True)
|
|
||||||
self.setWidget(self.label)
|
|
||||||
|
|
||||||
def show_data(self, data):
|
|
||||||
self.label.setText('')
|
|
||||||
self.data = data.copy()
|
|
||||||
rows = render_rows(self.data)
|
|
||||||
rows = u'\n'.join([u'<tr><td valign="top"><b>%s:</b></td><td valign="top">%s</td></tr>'%(k,t) for
|
|
||||||
k, t in rows])
|
|
||||||
self.label.setText(u'<table>%s</table>'%rows)
|
|
||||||
|
|
||||||
WEIGHTS = collections.defaultdict(lambda : 100)
|
WEIGHTS = collections.defaultdict(lambda : 100)
|
||||||
WEIGHTS[_('Path')] = 0
|
WEIGHTS[_('Path')] = 5
|
||||||
WEIGHTS[_('Formats')] = 1
|
WEIGHTS[_('Formats')] = 1
|
||||||
WEIGHTS[_('Collections')] = 2
|
WEIGHTS[_('Collections')] = 2
|
||||||
WEIGHTS[_('Series')] = 3
|
WEIGHTS[_('Series')] = 3
|
||||||
@ -86,7 +31,7 @@ def render_rows(data):
|
|||||||
rows = []
|
rows = []
|
||||||
for key in keys:
|
for key in keys:
|
||||||
txt = data[key]
|
txt = data[key]
|
||||||
if key in ('id', _('Comments')) or not txt or not txt.strip() or \
|
if key in ('id', _('Comments')) or not hasattr(txt, 'strip') or not txt.strip() or \
|
||||||
txt == 'None':
|
txt == 'None':
|
||||||
continue
|
continue
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
@ -97,20 +42,164 @@ def render_rows(data):
|
|||||||
txt = prepare_string_for_xml(txt)
|
txt = prepare_string_for_xml(txt)
|
||||||
if 'id' in data:
|
if 'id' in data:
|
||||||
if key == _('Path'):
|
if key == _('Path'):
|
||||||
txt = '...'+os.sep+os.sep.join(txt.split(os.sep)[-2:])
|
txt = u'<a href="path:%s" title="%s">%s</a>'%(data['id'],
|
||||||
txt = u'<a href="path:%s">%s</a>'%(data['id'], txt)
|
txt, _('Click to open'))
|
||||||
if key == _('Formats') and txt and txt != _('None'):
|
if key == _('Formats') and txt and txt != _('None'):
|
||||||
fmts = [x.strip() for x in txt.split(',')]
|
fmts = [x.strip() for x in txt.split(',')]
|
||||||
fmts = [u'<a href="format:%s:%s">%s</a>' % (data['id'], x, x) for x
|
fmts = [u'<a href="format:%s:%s">%s</a>' % (data['id'], x, x) for x
|
||||||
in fmts]
|
in fmts]
|
||||||
txt = ', '.join(fmts)
|
txt = ', '.join(fmts)
|
||||||
|
else:
|
||||||
|
if key == _('Path'):
|
||||||
|
txt = u'<a href="devpath:%s">%s</a>'%(txt,
|
||||||
|
_('Click to open'))
|
||||||
|
|
||||||
rows.append((key, txt))
|
rows.append((key, txt))
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class CoverView(QWidget): # {{{
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.setMaximumSize(QSize(120, 120))
|
||||||
|
self.setMinimumSize(QSize(120, 1))
|
||||||
|
self._current_pixmap_size = self.maximumSize()
|
||||||
|
|
||||||
|
self.animation = QPropertyAnimation(self, 'current_pixmap_size', self)
|
||||||
|
self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
|
||||||
|
self.animation.setDuration(1000)
|
||||||
|
self.animation.setStartValue(QSize(0, 0))
|
||||||
|
self.animation.valueChanged.connect(self.value_changed)
|
||||||
|
|
||||||
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
|
||||||
|
self.default_pixmap = QPixmap(I('book.svg'))
|
||||||
|
self.pixmap = self.default_pixmap
|
||||||
|
self.pwidth = self.pheight = None
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
|
self.do_layout()
|
||||||
|
|
||||||
|
def value_changed(self, val):
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setCurrentPixmapSize(self, val):
|
||||||
|
self._current_pixmap_size = val
|
||||||
|
|
||||||
|
def do_layout(self):
|
||||||
|
pixmap = self.pixmap
|
||||||
|
pwidth, pheight = pixmap.width(), pixmap.height()
|
||||||
|
self.pwidth, self.pheight = fit_image(pwidth, pheight,
|
||||||
|
self.rect().width(), self.rect().height())[1:]
|
||||||
|
self.current_pixmap_size = QSize(self.pwidth, self.pheight)
|
||||||
|
self.animation.setEndValue(self.current_pixmap_size)
|
||||||
|
|
||||||
|
def relayout(self, parent_size):
|
||||||
|
self.setMaximumSize(parent_size.width(),
|
||||||
|
min(int(parent_size.height()/2.),int(4/3. * parent_size.width())+1))
|
||||||
|
self.resize(self.maximumSize())
|
||||||
|
self.animation.stop()
|
||||||
|
self.do_layout()
|
||||||
|
|
||||||
|
def sizeHint(self):
|
||||||
|
return self.maximumSize()
|
||||||
|
|
||||||
|
def show_data(self, data):
|
||||||
|
self.animation.stop()
|
||||||
|
if data.get('id', None) == self.data.get('id', None):
|
||||||
|
return
|
||||||
|
self.data = {'id':data.get('id', None)}
|
||||||
|
if data.has_key('cover'):
|
||||||
|
self.pixmap = QPixmap.fromImage(data.pop('cover'))
|
||||||
|
if self.pixmap.isNull():
|
||||||
|
self.pixmap = self.default_pixmap
|
||||||
|
else:
|
||||||
|
self.pixmap = self.default_pixmap
|
||||||
|
self.do_layout()
|
||||||
|
self.update()
|
||||||
|
self.animation.start()
|
||||||
|
|
||||||
|
def paintEvent(self, event):
|
||||||
|
canvas_size = self.rect()
|
||||||
|
width = self.current_pixmap_size.width()
|
||||||
|
extrax = canvas_size.width() - width
|
||||||
|
if extrax < 0: extrax = 0
|
||||||
|
x = int(extrax/2.)
|
||||||
|
height = self.current_pixmap_size.height()
|
||||||
|
extray = canvas_size.height() - height
|
||||||
|
if extray < 0: extray = 0
|
||||||
|
y = int(extray/2.)
|
||||||
|
target = QRect(x, y, width, height)
|
||||||
|
p = QPainter(self)
|
||||||
|
p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
|
||||||
|
p.drawPixmap(target, self.pixmap.scaled(target.size(),
|
||||||
|
Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
current_pixmap_size = pyqtProperty('QSize',
|
||||||
|
fget=lambda self: self._current_pixmap_size,
|
||||||
|
fset=setCurrentPixmapSize
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class Label(QLabel):
|
||||||
|
|
||||||
|
mr = pyqtSignal(object)
|
||||||
|
link_clicked = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QLabel.__init__(self)
|
||||||
|
self.setTextFormat(Qt.RichText)
|
||||||
|
self.setText('')
|
||||||
|
self.setWordWrap(True)
|
||||||
|
self.linkActivated.connect(self.link_activated)
|
||||||
|
self._link_clicked = False
|
||||||
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
|
||||||
|
def link_activated(self, link):
|
||||||
|
self._link_clicked = True
|
||||||
|
link = unicode(link)
|
||||||
|
self.link_clicked.emit(link)
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, ev):
|
||||||
|
QLabel.mouseReleaseEvent(self, ev)
|
||||||
|
if not self._link_clicked:
|
||||||
|
self.mr.emit(ev)
|
||||||
|
self._link_clicked = False
|
||||||
|
|
||||||
|
class BookInfo(QScrollArea):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QScrollArea.__init__(self, parent)
|
||||||
|
self.setWidgetResizable(True)
|
||||||
|
self.label = Label()
|
||||||
|
self.setWidget(self.label)
|
||||||
|
self.link_clicked = self.label.link_clicked
|
||||||
|
self.mr = self.label.mr
|
||||||
|
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
def show_data(self, data):
|
||||||
|
self.label.setText('')
|
||||||
|
rows = render_rows(data)
|
||||||
|
rows = u'\n'.join([u'<tr><td valign="top"><b>%s:</b></td><td valign="top">%s</td></tr>'%(k,t) for
|
||||||
|
k, t in rows])
|
||||||
|
if _('Comments') in data and data[_('Comments')]:
|
||||||
|
comments = comments_to_html(data[_('Comments')])
|
||||||
|
rows += u'<tr><td colspan="2">%s</td></tr>'%comments
|
||||||
|
|
||||||
|
self.label.setText(u'<table>%s</table>'%rows)
|
||||||
|
|
||||||
class BookDetails(QWidget):
|
class BookDetails(QWidget):
|
||||||
|
|
||||||
resized = pyqtSignal(object)
|
resized = pyqtSignal(object)
|
||||||
show_book_info = pyqtSignal()
|
show_book_info = pyqtSignal()
|
||||||
|
open_containing_folder = pyqtSignal(int)
|
||||||
|
view_specific_format = pyqtSignal(int, object)
|
||||||
|
|
||||||
# Drag 'n drop {{{
|
# Drag 'n drop {{{
|
||||||
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS
|
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS
|
||||||
@ -151,21 +240,45 @@ class BookDetails(QWidget):
|
|||||||
|
|
||||||
self.setLayout(self._layout)
|
self.setLayout(self._layout)
|
||||||
self.cover_view = CoverView(self)
|
self.cover_view = CoverView(self)
|
||||||
self.cover_view.relayout()
|
self.cover_view.relayout(self.size())
|
||||||
self.resized.connect(self.cover_view.relayout, type=Qt.QueuedConnection)
|
self.resized.connect(self.cover_view.relayout, type=Qt.QueuedConnection)
|
||||||
self._layout.addWidget(self.cover_view)
|
self._layout.addWidget(self.cover_view, alignment=Qt.AlignHCenter)
|
||||||
|
self.book_info = BookInfo(self)
|
||||||
|
self._layout.addWidget(self.book_info)
|
||||||
|
self.book_info.link_clicked.connect(self._link_clicked)
|
||||||
|
self.book_info.mr.connect(self.mouseReleaseEvent)
|
||||||
|
self.setMinimumSize(QSize(190, 200))
|
||||||
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
|
|
||||||
|
def _link_clicked(self, link):
|
||||||
|
typ, _, val = link.partition(':')
|
||||||
|
if typ == 'path':
|
||||||
|
self.open_containing_folder.emit(int(val))
|
||||||
|
elif typ == 'format':
|
||||||
|
id_, fmt = val.split(':')
|
||||||
|
self.view_specific_format.emit(int(id_), fmt)
|
||||||
|
elif typ == 'devpath':
|
||||||
|
path = os.path.dirname(val)
|
||||||
|
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
||||||
|
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, ev):
|
||||||
|
ev.accept()
|
||||||
|
self.show_book_info.emit()
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
self.resized.emit(self.size())
|
self.resized.emit(self.size())
|
||||||
|
|
||||||
def show_data(self, data):
|
def show_data(self, data):
|
||||||
self.cover_view.show_data(data)
|
self.cover_view.show_data(data)
|
||||||
|
self.book_info.show_data(data)
|
||||||
|
self.setToolTip('<p>'+_('Click to open Book Details window') +
|
||||||
|
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), ''))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def reset_info(self):
|
def reset_info(self):
|
||||||
self.show_data({})
|
self.show_data({})
|
||||||
|
|
||||||
def mouseReleaseEvent(self, ev):
|
|
||||||
self.show_book_info.emit()
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -483,6 +483,14 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
|||||||
self.port.editingFinished.connect(self.check_port_value)
|
self.port.editingFinished.connect(self.check_port_value)
|
||||||
self.show_splash_screen.setChecked(gprefs.get('show_splash_screen',
|
self.show_splash_screen.setChecked(gprefs.get('show_splash_screen',
|
||||||
True))
|
True))
|
||||||
|
li = None
|
||||||
|
for i, z in enumerate([('wide', _('Wide')),
|
||||||
|
('narrow', _('Narrow'))]):
|
||||||
|
x, y = z
|
||||||
|
self.opt_gui_layout.addItem(y, QVariant(x))
|
||||||
|
if x == config['gui_layout']:
|
||||||
|
li = i
|
||||||
|
self.opt_gui_layout.setCurrentIndex(li)
|
||||||
|
|
||||||
def check_port_value(self, *args):
|
def check_port_value(self, *args):
|
||||||
port = self.port.value()
|
port = self.port.value()
|
||||||
@ -863,6 +871,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
|||||||
if self.viewer.item(i).checkState() == Qt.Checked:
|
if self.viewer.item(i).checkState() == Qt.Checked:
|
||||||
fmts.append(str(self.viewer.item(i).text()))
|
fmts.append(str(self.viewer.item(i).text()))
|
||||||
config['internally_viewed_formats'] = fmts
|
config['internally_viewed_formats'] = fmts
|
||||||
|
val = self.opt_gui_layout.itemData(self.opt_gui_layout.currentIndex()).toString()
|
||||||
|
config['gui_layout'] = unicode(val)
|
||||||
|
|
||||||
if not path or not os.path.exists(path) or not os.path.isdir(path):
|
if not path or not os.path.exists(path) or not os.path.isdir(path):
|
||||||
d = error_dialog(self, _('Invalid database location'),
|
d = error_dialog(self, _('Invalid database location'),
|
||||||
|
@ -89,8 +89,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>608</width>
|
<width>604</width>
|
||||||
<height>683</height>
|
<height>679</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_7">
|
<layout class="QGridLayout" name="gridLayout_7">
|
||||||
@ -332,7 +332,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page">
|
<widget class="QWidget" name="page">
|
||||||
<layout class="QGridLayout" name="gridLayout_8">
|
<layout class="QGridLayout" name="gridLayout_8">
|
||||||
<item row="0" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="roman_numerals">
|
<widget class="QCheckBox" name="roman_numerals">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use &Roman numerals for series number</string>
|
<string>Use &Roman numerals for series number</string>
|
||||||
@ -342,35 +342,35 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QCheckBox" name="systray_icon">
|
<widget class="QCheckBox" name="systray_icon">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable system &tray icon (needs restart)</string>
|
<string>Enable system &tray icon (needs restart)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="systray_notifications">
|
<widget class="QCheckBox" name="systray_notifications">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show &notifications in system tray</string>
|
<string>Show &notifications in system tray</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2">
|
<item row="3" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="show_splash_screen">
|
<widget class="QCheckBox" name="show_splash_screen">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show &splash screen at startup</string>
|
<string>Show &splash screen at startup</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="2">
|
<item row="4" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="separate_cover_flow">
|
<widget class="QCheckBox" name="separate_cover_flow">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show cover &browser in a separate window (needs restart)</string>
|
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QCheckBox" name="search_as_you_type">
|
<widget class="QCheckBox" name="search_as_you_type">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Search as you type</string>
|
<string>Search as you type</string>
|
||||||
@ -380,21 +380,21 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0" colspan="2">
|
<item row="6" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="sync_news">
|
<widget class="QCheckBox" name="sync_news">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Automatically send downloaded &news to ebook reader</string>
|
<string>Automatically send downloaded &news to ebook reader</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0" colspan="2">
|
<item row="7" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="delete_news">
|
<widget class="QCheckBox" name="delete_news">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Delete news from library when it is automatically sent to reader</string>
|
<string>&Delete news from library when it is automatically sent to reader</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0" colspan="2">
|
<item row="8" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
@ -411,7 +411,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="0" colspan="2">
|
<item row="9" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Toolbar</string>
|
<string>Toolbar</string>
|
||||||
@ -459,7 +459,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="0" colspan="2">
|
<item row="10" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
@ -625,6 +625,26 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_17">
|
||||||
|
<property name="text">
|
||||||
|
<string>User Interface &layout (needs restart):</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_gui_layout</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="opt_gui_layout">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>250</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page_6">
|
<widget class="QWidget" name="page_6">
|
||||||
|
@ -357,7 +357,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
aus = self.db.author_sort(row)
|
aus = self.db.author_sort(row)
|
||||||
self.author_sort.setText(aus if aus else '')
|
self.author_sort.setText(aus if aus else '')
|
||||||
tags = self.db.tags(row)
|
tags = self.db.tags(row)
|
||||||
self.tags.setText(', '.join(tags.split(',')) if tags else '')
|
self.original_tags = ', '.join(tags.split(',')) if tags else ''
|
||||||
|
self.tags.setText(self.original_tags)
|
||||||
self.tags.update_tags_cache(self.db.all_tags())
|
self.tags.update_tags_cache(self.db.all_tags())
|
||||||
rating = self.db.rating(row)
|
rating = self.db.rating(row)
|
||||||
if rating > 0:
|
if rating > 0:
|
||||||
@ -527,6 +528,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
self.publisher.setCurrentIndex(idx)
|
self.publisher.setCurrentIndex(idx)
|
||||||
|
|
||||||
def edit_tags(self):
|
def edit_tags(self):
|
||||||
|
if self.tags.text() != self.original_tags:
|
||||||
|
error_dialog(self, _('Cannot use tag editor'),
|
||||||
|
_('The tags editor cannot be used if you have modified the tags')).exec_()
|
||||||
|
return
|
||||||
d = TagEditor(self, self.db, self.row)
|
d = TagEditor(self, self.db, self.row)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
if d.result() == QDialog.Accepted:
|
if d.result() == QDialog.Accepted:
|
||||||
|
@ -17,6 +17,8 @@ from calibre.gui2 import config, is_widescreen
|
|||||||
from calibre.gui2.library.views import BooksView, DeviceBooksView
|
from calibre.gui2.library.views import BooksView, DeviceBooksView
|
||||||
from calibre.gui2.widgets import Splitter
|
from calibre.gui2.widgets import Splitter
|
||||||
from calibre.gui2.tag_view import TagBrowserWidget
|
from calibre.gui2.tag_view import TagBrowserWidget
|
||||||
|
from calibre.gui2.status import StatusBar, HStatusBar
|
||||||
|
from calibre.gui2.book_details import BookDetails
|
||||||
|
|
||||||
_keep_refs = []
|
_keep_refs = []
|
||||||
|
|
||||||
@ -290,9 +292,9 @@ class LibraryViewMixin(object): # {{{
|
|||||||
class LibraryWidget(Splitter): # {{{
|
class LibraryWidget(Splitter): # {{{
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
orientation = Qt.Vertical if config['gui_layout'] == 'narrow' and \
|
orientation = Qt.Vertical
|
||||||
not is_widescreen() else Qt.Horizontal
|
if config['gui_layout'] == 'narrow':
|
||||||
#orientation = Qt.Vertical
|
orientation = Qt.Horizontal if is_widescreen() else Qt.Vertical
|
||||||
idx = 0 if orientation == Qt.Vertical else 1
|
idx = 0 if orientation == Qt.Vertical else 1
|
||||||
size = 300 if orientation == Qt.Vertical else 550
|
size = 300 if orientation == Qt.Vertical else 550
|
||||||
Splitter.__init__(self, 'cover_browser_splitter', _('Cover Browser'),
|
Splitter.__init__(self, 'cover_browser_splitter', _('Cover Browser'),
|
||||||
@ -360,7 +362,6 @@ class LayoutMixin(object): # {{{
|
|||||||
self.setWindowTitle(__appname__)
|
self.setWindowTitle(__appname__)
|
||||||
|
|
||||||
if config['gui_layout'] == 'narrow':
|
if config['gui_layout'] == 'narrow':
|
||||||
from calibre.gui2.status import StatusBar
|
|
||||||
self.status_bar = self.book_details = StatusBar(self)
|
self.status_bar = self.book_details = StatusBar(self)
|
||||||
self.stack = Stack(self)
|
self.stack = Stack(self)
|
||||||
self.bd_splitter = Splitter('book_details_splitter',
|
self.bd_splitter = Splitter('book_details_splitter',
|
||||||
@ -375,8 +376,28 @@ class LayoutMixin(object): # {{{
|
|||||||
l.addWidget(self.sidebar)
|
l.addWidget(self.sidebar)
|
||||||
self.bd_splitter.addWidget(self._layout_mem[0])
|
self.bd_splitter.addWidget(self._layout_mem[0])
|
||||||
self.bd_splitter.addWidget(self.status_bar)
|
self.bd_splitter.addWidget(self.status_bar)
|
||||||
self.bd_splitter.setCollapsible((self.bd_splitter.side_index+1)%2, False)
|
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
|
||||||
self.centralwidget.layout().addWidget(self.bd_splitter)
|
self.centralwidget.layout().addWidget(self.bd_splitter)
|
||||||
|
else:
|
||||||
|
self.status_bar = HStatusBar(self)
|
||||||
|
self.setStatusBar(self.status_bar)
|
||||||
|
self.bd_splitter = Splitter('book_details_splitter',
|
||||||
|
_('Book Details'), I('book.svg'), initial_side_size=200,
|
||||||
|
orientation=Qt.Horizontal, parent=self, side_index=1)
|
||||||
|
self.stack = Stack(self)
|
||||||
|
self.bd_splitter.addWidget(self.stack)
|
||||||
|
self.book_details = BookDetails(self)
|
||||||
|
self.bd_splitter.addWidget(self.book_details)
|
||||||
|
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
|
||||||
|
self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
|
||||||
|
QSizePolicy.Expanding))
|
||||||
|
self.centralwidget.layout().addWidget(self.bd_splitter)
|
||||||
|
|
||||||
|
for x in ('cb', 'tb', 'bd'):
|
||||||
|
button = getattr(self, x+'_splitter').button
|
||||||
|
button.setIconSize(QSize(22, 22))
|
||||||
|
self.status_bar.addPermanentWidget(button)
|
||||||
|
self.status_bar.addPermanentWidget(self.jobs_button)
|
||||||
|
|
||||||
def finalize_layout(self):
|
def finalize_layout(self):
|
||||||
m = self.library_view.model()
|
m = self.library_view.model()
|
||||||
|
@ -274,11 +274,15 @@ class JobsButton(QFrame):
|
|||||||
|
|
||||||
def __init__(self, horizontal=False, size=48, parent=None):
|
def __init__(self, horizontal=False, size=48, parent=None):
|
||||||
QFrame.__init__(self, parent)
|
QFrame.__init__(self, parent)
|
||||||
|
if horizontal:
|
||||||
|
size = 24
|
||||||
self.pi = ProgressIndicator(self, size)
|
self.pi = ProgressIndicator(self, size)
|
||||||
self._jobs = QLabel('<b>'+_('Jobs:')+' 0')
|
self._jobs = QLabel('<b>'+_('Jobs:')+' 0')
|
||||||
|
self._jobs.mouseReleaseEvent = self.mouseReleaseEvent
|
||||||
|
|
||||||
if horizontal:
|
if horizontal:
|
||||||
self.setLayout(QHBoxLayout())
|
self.setLayout(QHBoxLayout())
|
||||||
|
self.layout().setDirection(self.layout().RightToLeft)
|
||||||
else:
|
else:
|
||||||
self.setLayout(QVBoxLayout())
|
self.setLayout(QVBoxLayout())
|
||||||
self._jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom)
|
self._jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom)
|
||||||
|
@ -554,7 +554,7 @@ void PictureFlowPrivate::resize(int w, int h)
|
|||||||
if (h < 10) h = 10;
|
if (h < 10) h = 10;
|
||||||
slideHeight = int(float(h)/REFLECTION_FACTOR);
|
slideHeight = int(float(h)/REFLECTION_FACTOR);
|
||||||
slideWidth = int(float(slideHeight) * 2/3.);
|
slideWidth = int(float(slideHeight) * 2/3.);
|
||||||
fontSize = MAX(int(h/20.), 12);
|
fontSize = MAX(int(h/15.), 12);
|
||||||
recalc(w, h);
|
recalc(w, h);
|
||||||
resetSlides();
|
resetSlides();
|
||||||
triggerRender();
|
triggerRender();
|
||||||
|
@ -5,7 +5,7 @@ import os
|
|||||||
|
|
||||||
from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
|
from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
|
||||||
QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \
|
QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \
|
||||||
QPropertyAnimation, QEasingCurve
|
QPropertyAnimation, QEasingCurve, QDesktopServices, QUrl
|
||||||
|
|
||||||
|
|
||||||
from calibre import fit_image, preferred_encoding, isosx
|
from calibre import fit_image, preferred_encoding, isosx
|
||||||
@ -49,7 +49,7 @@ class BookInfoDisplay(QWidget):
|
|||||||
event.acceptProposedAction()
|
event.acceptProposedAction()
|
||||||
|
|
||||||
|
|
||||||
class BookCoverDisplay(QLabel):
|
class BookCoverDisplay(QLabel): # {{{
|
||||||
|
|
||||||
def __init__(self, coverpath=I('book.svg')):
|
def __init__(self, coverpath=I('book.svg')):
|
||||||
QLabel.__init__(self)
|
QLabel.__init__(self)
|
||||||
@ -90,6 +90,7 @@ class BookInfoDisplay(QWidget):
|
|||||||
self.statusbar_height = statusbar_size.height()
|
self.statusbar_height = statusbar_size.height()
|
||||||
self.do_layout()
|
self.do_layout()
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class BookDataDisplay(QLabel):
|
class BookDataDisplay(QLabel):
|
||||||
|
|
||||||
@ -151,7 +152,7 @@ class BookInfoDisplay(QWidget):
|
|||||||
k, t in rows])
|
k, t in rows])
|
||||||
if _('Comments') in self.data:
|
if _('Comments') in self.data:
|
||||||
comments = comments_to_html(self.data[_('Comments')])
|
comments = comments_to_html(self.data[_('Comments')])
|
||||||
comments = '<b>Comments:</b>'+comments
|
comments = ('<b>%s:</b>'%_('Comments'))+comments
|
||||||
left_pane = u'<table>%s</table>'%rows
|
left_pane = u'<table>%s</table>'%rows
|
||||||
right_pane = u'<div>%s</div>'%comments
|
right_pane = u'<div>%s</div>'%comments
|
||||||
self.book_data.setText(u'<table><tr><td valign="top" '
|
self.book_data.setText(u'<table><tr><td valign="top" '
|
||||||
@ -162,6 +163,10 @@ class BookInfoDisplay(QWidget):
|
|||||||
self.book_data.updateGeometry()
|
self.book_data.updateGeometry()
|
||||||
self.updateGeometry()
|
self.updateGeometry()
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
|
self.setToolTip('<p>'+_('Click to open Book Details window') +
|
||||||
|
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), ''))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class StatusBarInterface(object):
|
class StatusBarInterface(object):
|
||||||
|
|
||||||
@ -196,6 +201,9 @@ class BookDetailsInterface(object):
|
|||||||
def show_data(self, data):
|
def show_data(self, data):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
class HStatusBar(QStatusBar, StatusBarInterface):
|
||||||
|
pass
|
||||||
|
|
||||||
class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
|
class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
|
||||||
|
|
||||||
files_dropped = pyqtSignal(object, object)
|
files_dropped = pyqtSignal(object, object)
|
||||||
@ -227,9 +235,13 @@ class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
|
|||||||
typ, _, val = link.partition(':')
|
typ, _, val = link.partition(':')
|
||||||
if typ == 'path':
|
if typ == 'path':
|
||||||
self.open_containing_folder.emit(int(val))
|
self.open_containing_folder.emit(int(val))
|
||||||
if typ == 'format':
|
elif typ == 'format':
|
||||||
id_, fmt = val.split(':')
|
id_, fmt = val.split(':')
|
||||||
self.view_specific_format.emit(int(id_), fmt)
|
self.view_specific_format.emit(int(id_), fmt)
|
||||||
|
elif typ == 'devpath':
|
||||||
|
path = os.path.dirname(val)
|
||||||
|
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
||||||
|
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
self.resized.emit(self.size())
|
self.resized.emit(self.size())
|
||||||
|
@ -126,7 +126,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{
|
|||||||
# Jobs Button {{{
|
# Jobs Button {{{
|
||||||
self.job_manager = JobManager()
|
self.job_manager = JobManager()
|
||||||
self.jobs_dialog = JobsDialog(self, self.job_manager)
|
self.jobs_dialog = JobsDialog(self, self.job_manager)
|
||||||
self.jobs_button = JobsButton()
|
self.jobs_button = JobsButton(horizontal=config['gui_layout'] !=
|
||||||
|
'narrow')
|
||||||
self.jobs_button.initialize(self.jobs_dialog, self.job_manager)
|
self.jobs_button.initialize(self.jobs_dialog, self.job_manager)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -1110,6 +1110,8 @@ class Splitter(QSplitter):
|
|||||||
def show_side_pane(self):
|
def show_side_pane(self):
|
||||||
if self.count() < 2 or not self.is_side_index_hidden:
|
if self.count() < 2 or not self.is_side_index_hidden:
|
||||||
return
|
return
|
||||||
|
if self.desired_side_size == 0:
|
||||||
|
self.desired_side_size = self.initial_side_size
|
||||||
self.apply_state((True, self.desired_side_size))
|
self.apply_state((True, self.desired_side_size))
|
||||||
|
|
||||||
def hide_side_pane(self):
|
def hide_side_pane(self):
|
||||||
|
@ -291,4 +291,8 @@ class SchemaUpgrade(object):
|
|||||||
|
|
||||||
for field in self.field_metadata.itervalues():
|
for field in self.field_metadata.itervalues():
|
||||||
if field['is_category'] and not field['is_custom'] and 'link_column' in field:
|
if field['is_category'] and not field['is_custom'] and 'link_column' in field:
|
||||||
create_tag_browser_view(field['table'], field['link_column'], field['column'])
|
table = self.conn.get(
|
||||||
|
'SELECT name FROM sqlite_master WHERE type="table" AND name=?',
|
||||||
|
('books_%s_link'%field['table'],), all=False)
|
||||||
|
if table is not None:
|
||||||
|
create_tag_browser_view(field['table'], field['link_column'], field['column'])
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user