KG updates

This commit is contained in:
GRiker 2010-06-15 04:15:10 -06:00
commit 14bdeba5e3
17 changed files with 1306 additions and 899 deletions

View 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')
]

View 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')
]

View File

@ -7,12 +7,33 @@ __docformat__ = 'restructuredtext en'
'''
Device driver for Amazon's Kindle
'''
import datetime, os, re, sys
import datetime, os, re, sys, json, hashlib
from cStringIO import StringIO
from struct import unpack
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):
name = 'Kindle Device Interface'
@ -60,6 +81,7 @@ class KINDLE(USBMS):
'replace')
return mi
def get_annotations(self, path_map):
MBP_FORMATS = [u'azw', u'mobi', u'prc', u'txt']
mbp_formats = set(MBP_FORMATS)
@ -150,6 +172,37 @@ class KINDLE2(KINDLE):
PRODUCT_ID = [0x0002]
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):

View File

@ -10,12 +10,31 @@ import os
from contextlib import closing
from calibre.customize import FileTypePlugin
from calibre.utils.zipfile import ZipFile, stringFileHeader
def is_comic(list_of_names):
extensions = set([x.rpartition('.')[-1].lower() for x in list_of_names])
comic_extensions = set(['jpg', 'jpeg', 'png'])
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):
name = 'Archive Extract'
author = 'Kovid Goyal'
@ -31,7 +50,6 @@ class ArchiveExtract(FileTypePlugin):
if is_rar:
from calibre.libunrar import extract_member, names
else:
from calibre.utils.zipfile import ZipFile
zf = ZipFile(archive, 'r')
if is_rar:

View File

@ -100,7 +100,7 @@ def _config():
c.add_opt('tag_browser_hidden_categories', default=set(),
help=_('tag browser categories not to display'))
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)
config = _config()

View File

@ -8,73 +8,18 @@ __docformat__ = 'restructuredtext en'
import os, collections
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.gui2.widgets import IMAGE_EXTENSIONS
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.constants import preferred_encoding
from calibre.library.comments import comments_to_html
class CoverView(QLabel):
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)
# render_rows(data) {{{
WEIGHTS = collections.defaultdict(lambda : 100)
WEIGHTS[_('Path')] = 0
WEIGHTS[_('Path')] = 5
WEIGHTS[_('Formats')] = 1
WEIGHTS[_('Collections')] = 2
WEIGHTS[_('Series')] = 3
@ -86,7 +31,7 @@ def render_rows(data):
rows = []
for key in keys:
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':
continue
if isinstance(key, str):
@ -97,20 +42,164 @@ def render_rows(data):
txt = prepare_string_for_xml(txt)
if 'id' in data:
if key == _('Path'):
txt = '...'+os.sep+os.sep.join(txt.split(os.sep)[-2:])
txt = u'<a href="path:%s">%s</a>'%(data['id'], txt)
txt = u'<a href="path:%s" title="%s">%s</a>'%(data['id'],
txt, _('Click to open'))
if key == _('Formats') and txt and txt != _('None'):
fmts = [x.strip() for x in txt.split(',')]
fmts = [u'<a href="format:%s:%s">%s</a>' % (data['id'], x, x) for x
in fmts]
txt = ', '.join(fmts)
else:
if key == _('Path'):
txt = u'<a href="devpath:%s">%s</a>'%(txt,
_('Click to open'))
rows.append((key, txt))
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):
resized = pyqtSignal(object)
show_book_info = pyqtSignal()
open_containing_folder = pyqtSignal(int)
view_specific_format = pyqtSignal(int, object)
# Drag 'n drop {{{
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS
@ -151,21 +240,45 @@ class BookDetails(QWidget):
self.setLayout(self._layout)
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._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):
self.resized.emit(self.size())
def show_data(self, 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):
self.show_data({})
def mouseReleaseEvent(self, ev):
self.show_book_info.emit()

View File

@ -483,6 +483,14 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
self.port.editingFinished.connect(self.check_port_value)
self.show_splash_screen.setChecked(gprefs.get('show_splash_screen',
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):
port = self.port.value()
@ -863,6 +871,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
if self.viewer.item(i).checkState() == Qt.Checked:
fmts.append(str(self.viewer.item(i).text()))
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):
d = error_dialog(self, _('Invalid database location'),

View File

@ -89,8 +89,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>608</width>
<height>683</height>
<width>604</width>
<height>679</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_7">
@ -332,7 +332,7 @@
</widget>
<widget class="QWidget" name="page">
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<item row="1" column="0">
<widget class="QCheckBox" name="roman_numerals">
<property name="text">
<string>Use &amp;Roman numerals for series number</string>
@ -342,35 +342,35 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QCheckBox" name="systray_icon">
<property name="text">
<string>Enable system &amp;tray icon (needs restart)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QCheckBox" name="systray_notifications">
<property name="text">
<string>Show &amp;notifications in system tray</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="show_splash_screen">
<property name="text">
<string>Show &amp;splash screen at startup</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="separate_cover_flow">
<property name="text">
<string>Show cover &amp;browser in a separate window (needs restart)</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QCheckBox" name="search_as_you_type">
<property name="text">
<string>Search as you type</string>
@ -380,21 +380,21 @@
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="sync_news">
<property name="text">
<string>Automatically send downloaded &amp;news to ebook reader</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<item row="7" column="0" colspan="2">
<widget class="QCheckBox" name="delete_news">
<property name="text">
<string>&amp;Delete news from library when it is automatically sent to reader</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<item row="8" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_6">
@ -411,7 +411,7 @@
</item>
</layout>
</item>
<item row="8" column="0" colspan="2">
<item row="9" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Toolbar</string>
@ -459,7 +459,7 @@
</layout>
</widget>
</item>
<item row="9" column="0" colspan="2">
<item row="10" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QGroupBox" name="groupBox">
@ -625,6 +625,26 @@
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>User Interface &amp;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>
</widget>
<widget class="QWidget" name="page_6">

View File

@ -357,7 +357,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
aus = self.db.author_sort(row)
self.author_sort.setText(aus if aus else '')
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())
rating = self.db.rating(row)
if rating > 0:
@ -527,6 +528,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.publisher.setCurrentIndex(idx)
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.exec_()
if d.result() == QDialog.Accepted:

View File

@ -17,6 +17,8 @@ from calibre.gui2 import config, is_widescreen
from calibre.gui2.library.views import BooksView, DeviceBooksView
from calibre.gui2.widgets import Splitter
from calibre.gui2.tag_view import TagBrowserWidget
from calibre.gui2.status import StatusBar, HStatusBar
from calibre.gui2.book_details import BookDetails
_keep_refs = []
@ -290,9 +292,9 @@ class LibraryViewMixin(object): # {{{
class LibraryWidget(Splitter): # {{{
def __init__(self, parent):
orientation = Qt.Vertical if config['gui_layout'] == 'narrow' and \
not is_widescreen() else Qt.Horizontal
#orientation = Qt.Vertical
orientation = Qt.Vertical
if config['gui_layout'] == 'narrow':
orientation = Qt.Horizontal if is_widescreen() else Qt.Vertical
idx = 0 if orientation == Qt.Vertical else 1
size = 300 if orientation == Qt.Vertical else 550
Splitter.__init__(self, 'cover_browser_splitter', _('Cover Browser'),
@ -360,7 +362,6 @@ class LayoutMixin(object): # {{{
self.setWindowTitle(__appname__)
if config['gui_layout'] == 'narrow':
from calibre.gui2.status import StatusBar
self.status_bar = self.book_details = StatusBar(self)
self.stack = Stack(self)
self.bd_splitter = Splitter('book_details_splitter',
@ -375,8 +376,28 @@ class LayoutMixin(object): # {{{
l.addWidget(self.sidebar)
self.bd_splitter.addWidget(self._layout_mem[0])
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)
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):
m = self.library_view.model()

View File

@ -274,11 +274,15 @@ class JobsButton(QFrame):
def __init__(self, horizontal=False, size=48, parent=None):
QFrame.__init__(self, parent)
if horizontal:
size = 24
self.pi = ProgressIndicator(self, size)
self._jobs = QLabel('<b>'+_('Jobs:')+' 0')
self._jobs.mouseReleaseEvent = self.mouseReleaseEvent
if horizontal:
self.setLayout(QHBoxLayout())
self.layout().setDirection(self.layout().RightToLeft)
else:
self.setLayout(QVBoxLayout())
self._jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom)

View File

@ -554,7 +554,7 @@ void PictureFlowPrivate::resize(int w, int h)
if (h < 10) h = 10;
slideHeight = int(float(h)/REFLECTION_FACTOR);
slideWidth = int(float(slideHeight) * 2/3.);
fontSize = MAX(int(h/20.), 12);
fontSize = MAX(int(h/15.), 12);
recalc(w, h);
resetSlides();
triggerRender();

View File

@ -5,7 +5,7 @@ import os
from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \
QPropertyAnimation, QEasingCurve
QPropertyAnimation, QEasingCurve, QDesktopServices, QUrl
from calibre import fit_image, preferred_encoding, isosx
@ -49,7 +49,7 @@ class BookInfoDisplay(QWidget):
event.acceptProposedAction()
class BookCoverDisplay(QLabel):
class BookCoverDisplay(QLabel): # {{{
def __init__(self, coverpath=I('book.svg')):
QLabel.__init__(self)
@ -90,6 +90,7 @@ class BookInfoDisplay(QWidget):
self.statusbar_height = statusbar_size.height()
self.do_layout()
# }}}
class BookDataDisplay(QLabel):
@ -151,7 +152,7 @@ class BookInfoDisplay(QWidget):
k, t in rows])
if _('Comments') in self.data:
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
right_pane = u'<div>%s</div>'%comments
self.book_data.setText(u'<table><tr><td valign="top" '
@ -162,6 +163,10 @@ class BookInfoDisplay(QWidget):
self.book_data.updateGeometry()
self.updateGeometry()
self.setVisible(True)
self.setToolTip('<p>'+_('Click to open Book Details window') +
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), ''))
class StatusBarInterface(object):
@ -196,6 +201,9 @@ class BookDetailsInterface(object):
def show_data(self, data):
raise NotImplementedError()
class HStatusBar(QStatusBar, StatusBarInterface):
pass
class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
files_dropped = pyqtSignal(object, object)
@ -227,9 +235,13 @@ class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
typ, _, val = link.partition(':')
if typ == 'path':
self.open_containing_folder.emit(int(val))
if typ == 'format':
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 resizeEvent(self, ev):
self.resized.emit(self.size())

View File

@ -126,7 +126,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{
# Jobs Button {{{
self.job_manager = JobManager()
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)
# }}}

View File

@ -1110,6 +1110,8 @@ class Splitter(QSplitter):
def show_side_pane(self):
if self.count() < 2 or not self.is_side_index_hidden:
return
if self.desired_side_size == 0:
self.desired_side_size = self.initial_side_size
self.apply_state((True, self.desired_side_size))
def hide_side_pane(self):

View File

@ -291,4 +291,8 @@ class SchemaUpgrade(object):
for field in self.field_metadata.itervalues():
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