Merge from trunk

This commit is contained in:
Charles Haley 2010-06-12 10:58:16 +01:00
commit 90042d7d40
14 changed files with 931 additions and 810 deletions

View File

@ -4,6 +4,65 @@
# for important features/bug fixes.
# Also, each release can have new and improved recipes.
- version: 0.7.2
date: 2010-06-11
new features:
- title: "The Cover Browser can now be freely resized."
description: >
"You can now resize the Cover Browser just like the other areas of the user interface by dragging the edge. The Cover Browser now
also emphasizes the cetral book cover, making it larger than the others. Also on widescreen monitors the cover browser is now automatically placed
to the side of the book list instead of below it."
- title: "Added tweak to control how titles and series names are sorted"
- title: "Clicking on row numbers no longer open the viewer. Instead you have to double click"
bug fixes:
- title: "SONY driver: The regression causing slow perfomance has been corrected. Various bug fixes to deal with corner cases"
- title: "iPad driver: Various bugfixes, should now work much more seamlessly."
- title: "Fix regression causing calibre to not start if the library path is invalid, because say a drive has been removed"
tickets: [5787]
- title: "Fix regression in 0.7.1 that broke searching in the e-book viewers and for news sources"
- title: "Fix regressions that caused the Publisher to change when updating Series and floating point custom columns to be rounded to integers."
tickets: [5788]
- title: "Fix regression that broke check database integrity in the presence of custom coulmns or user categories"
tickets: [5779]
- title: "Fix regresison that broke the Email to submenu in the send to device menu"
- title: "Fix Tag browser re-opening closed tree after editing metadata"
tickets: [5744]
- title: "Conversion pipeline: Handle missing/obsolete input/output profiles gracefully"
- title: "Fix Adding/Deleting Search does not refresh Left Pane Correctly"
tickets: [5751]
- title: "Content server: Fix serving of CBZ/CBR files to stanza"
new recipes:
- title: Repantes, Haaretz
author: Darko Miletic
- title: CBC Canada
author: rty
improved recipes:
- The Sun
- The Economist
- Boston Globe
- Honolulu Star Advertiser
- SMH
- Sueddeutsche
- Our Daily Bread
- version: 0.7.1
date: 2010-06-04

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.7.1'
__version__ = '0.7.2'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -445,7 +445,7 @@ from calibre.devices.nook.driver import NOOK
from calibre.devices.prs505.driver import PRS505
from calibre.devices.android.driver import ANDROID, S60
from calibre.devices.nokia.driver import N770, N810, E71X
from calibre.devices.eslick.driver import ESLICK
from calibre.devices.eslick.driver import ESLICK, EBK52
from calibre.devices.nuut2.driver import NUUT2
from calibre.devices.iriver.driver import IRIVER_STORY
from calibre.devices.binatone.driver import README
@ -519,6 +519,7 @@ plugins += [
N810,
COOL_ER,
ESLICK,
EBK52,
NUUT2,
IRIVER_STORY,
GER2,

View File

@ -14,7 +14,6 @@ from calibre.devices.interface import DevicePlugin
from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata import MetaInformation
from calibre.library.server.utils import strftime
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import Config, config_dir
from calibre.utils.date import parse_date
from calibre.utils.logging import Log
@ -988,7 +987,6 @@ class ITUNES(DevicePlugin):
connected_device = self.sources['iPod']
device = self.iTunes.sources.ItemByName(connected_device)
dev_books = None
added = None
for pl in device.Playlists:
if pl.Kind == self.PlaylistKind.index('User') and \
@ -1641,7 +1639,6 @@ class ITUNES(DevicePlugin):
connected_device = self.sources['iPod']
device = self.iTunes.sources.ItemByName(connected_device)
dev_books = None
for pl in device.Playlists:
if pl.Kind == self.PlaylistKind.index('User') and \
pl.SpecialKind == self.PlaylistSpecialKind.index('Books'):
@ -1988,7 +1985,7 @@ class ITUNES(DevicePlugin):
if isosx:
if DEBUG:
self.log.info(" deleting %s" % cached_book['dev_book'])
result = cached_book['dev_book'].delete()
cached_book['dev_book'].delete()
elif iswindows:
dev_pl = self._get_device_books_playlist()
@ -2000,7 +1997,7 @@ class ITUNES(DevicePlugin):
if hit.Name == cached_book['title']:
if DEBUG:
self.log.info(" deleting '%s' by %s" % (hit.Name, hit.Artist))
results = hit.Delete()
hit.Delete()
break
def _remove_from_iTunes(self, cached_book):

View File

@ -36,4 +36,29 @@ class ESLICK(USBMS):
SUPPORTS_SUB_DIRS = True
@classmethod
def can_handle(cls, dev, debug=False):
return (dev[3], dev[4]) != ('philips', 'Philips d')
class EBK52(ESLICK):
name = 'EBK-52 Device Interface'
gui_name = 'Sigmatek EBK'
description = _('Communicate with the Sigmatek eBook reader.')
FORMATS = ['epub', 'fb2', 'pdf', 'txt']
VENDOR_NAME = ''
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOK_READER'
MAIN_MEMORY_VOLUME_LABEL = 'Sigmatek Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'Sigmatek Storage Card'
@classmethod
def can_handle(cls, dev, debug=False):
return (dev[3], dev[4]) == ('philips', 'Philips d')

View File

@ -687,7 +687,7 @@ class DeviceMixin(object):
self.emailer.send_mails(jobnames,
Dispatcher(partial(self.emails_sent, remove=remove)),
attachments, to_s, subjects, texts, attachment_names)
self.status_bar.showMessage(_('Sending email to')+' '+to, 3000)
self.status_bar.show_message(_('Sending email to')+' '+to, 3000)
auto = []
if _auto_ids != []:
@ -748,7 +748,7 @@ class DeviceMixin(object):
'%s'%errors, show=True
)
else:
self.status_bar.showMessage(_('Sent by email:') + ', '.join(good),
self.status_bar.show_message(_('Sent by email:') + ', '.join(good),
5000)
def cover_to_thumbnail(self, data):
@ -787,7 +787,7 @@ class DeviceMixin(object):
attachments, to_s, subjects, texts, attachment_names)
sent_mails.append(to_s[0])
if sent_mails:
self.status_bar.showMessage(_('Sent news to')+' '+\
self.status_bar.show_message(_('Sent news to')+' '+\
', '.join(sent_mails), 3000)
def sync_catalogs(self, send_ids=None, do_auto_convert=True):
@ -846,7 +846,7 @@ class DeviceMixin(object):
self.upload_books(files, names, metadata,
on_card=on_card,
memory=[files, remove])
self.status_bar.showMessage(_('Sending catalogs to device.'), 5000)
self.status_bar.show_message(_('Sending catalogs to device.'), 5000)
@ -909,7 +909,7 @@ class DeviceMixin(object):
self.upload_books(files, names, metadata,
on_card=on_card,
memory=[files, remove])
self.status_bar.showMessage(_('Sending news to device.'), 5000)
self.status_bar.show_message(_('Sending news to device.'), 5000)
def sync_to_device(self, on_card, delete_from_library,
@ -963,7 +963,7 @@ class DeviceMixin(object):
names.append('%s_%d%s'%(prefix, id, os.path.splitext(f)[1]))
remove = remove_ids if delete_from_library else []
self.upload_books(gf, names, good, on_card, memory=(_files, remove))
self.status_bar.showMessage(_('Sending books to device.'), 5000)
self.status_bar.show_message(_('Sending books to device.'), 5000)
auto = []
if _auto_ids != []:

View File

@ -49,12 +49,12 @@ class BookInfo(QDialog, Ui_BookInfo):
def open_book_path(self, path):
if os.sep in unicode(path):
QDesktopServices.openUrl(QUrl('file:'+path))
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
else:
format = unicode(path)
path = self.view.model().db.format_abspath(self.current_row, format)
if path is not None:
QDesktopServices.openUrl(QUrl('file:'+path))
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
def next(self):

View File

@ -132,6 +132,7 @@ class ToolbarMixin(object): # {{{
self.action_open_containing_folder.setShortcut(Qt.Key_O)
self.addAction(self.action_open_containing_folder)
self.action_open_containing_folder.triggered.connect(self.view_folder)
self.action_sync.setShortcut(Qt.Key_D)
self.action_sync.setEnabled(True)
self.create_device_menu()
@ -231,7 +232,7 @@ class LibraryViewMixin(object): # {{{
('connect_to_search_box', (self.search,
self.search_done)),
('connect_to_book_display',
(self.status_bar.book_info.show_data,)),
(self.book_details.show_data,)),
]:
for view in (self.library_view, self.memory_view, self.card_a_view, self.card_b_view):
getattr(view, func)(*args)
@ -360,7 +361,7 @@ class LayoutMixin(object): # {{{
if config['gui_layout'] == 'narrow':
from calibre.gui2.status import StatusBar
self.status_bar = StatusBar(self)
self.status_bar = self.book_details = StatusBar(self)
self.stack = Stack(self)
self.bd_splitter = Splitter('book_details_splitter',
_('Book Details'), I('book.svg'),

View File

@ -292,7 +292,8 @@ class BooksView(QTableView): # {{{
old_state['column_positions'][name] = i
if name != 'ondevice':
old_state['column_sizes'][name] = \
max(self.sizeHintForColumn(i), h.sectionSizeHint(i))
min(350, max(self.sizeHintForColumn(i),
h.sectionSizeHint(i)))
if name == 'timestamp':
old_state['column_sizes'][name] += 12
return old_state

View File

@ -7,8 +7,8 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import QComboBox, Qt, QLineEdit, QStringList, pyqtSlot, \
pyqtSignal, SIGNAL, QObject, QDialog
from PyQt4.QtGui import QCompleter
pyqtSignal, SIGNAL, QObject, QDialog, QCompleter, \
QAction, QKeySequence
from calibre.gui2 import config
from calibre.gui2.dialogs.confirm_delete import confirm
@ -348,8 +348,14 @@ class SearchBoxMixin(object):
self.do_advanced_search)
self.search.clear()
self.search.setFocus(Qt.OtherFocusReason)
self.search.setMaximumWidth(self.width()-150)
self.action_focus_search = QAction(self)
shortcuts = QKeySequence.keyBindings(QKeySequence.Find)
shortcuts = list(shortcuts) + [QKeySequence('/')]
self.action_focus_search.setShortcuts(shortcuts)
self.action_focus_search.triggered.connect(lambda x:
self.search.setFocus(Qt.OtherFocusReason))
self.addAction(self.action_focus_search)
def search_box_cleared(self):
self.tags_view.clear()

View File

@ -61,7 +61,7 @@ class BookInfoDisplay(QWidget):
pixmap = self.pixmap()
pwidth, pheight = pixmap.width(), pixmap.height()
width, height = fit_image(pwidth, pheight,
pwidth, self.statusbar_height-12)[1:]
pwidth, self.statusbar_height-20)[1:]
self.setMaximumHeight(height)
try:
aspect_ratio = pwidth/float(pheight)
@ -162,17 +162,48 @@ class BookInfoDisplay(QWidget):
self.updateGeometry()
self.setVisible(True)
class StatusBar(QStatusBar):
resized = pyqtSignal(object)
files_dropped = pyqtSignal(object, object)
show_book_info = pyqtSignal()
class StatusBarInterface(object):
def initialize(self, systray=None):
self.systray = systray
self.notifier = get_notifier(systray)
self.book_info = BookInfoDisplay(self.clearMessage)
def show_message(self, msg, timeout=0):
QStatusBar.showMessage(self, msg, timeout)
if self.notifier is not None and not config['disable_tray_notification']:
if isosx and isinstance(msg, unicode):
try:
msg = msg.encode(preferred_encoding)
except UnicodeEncodeError:
msg = msg.encode('utf-8')
self.notifier(msg)
def clear_message(self):
QStatusBar.clearMessage(self)
class BookDetailsInterface(object):
# These signals must be defined in the class implementing this interface
files_dropped = None
show_book_info = None
def reset_info(self):
raise NotImplementedError()
def show_data(self, data):
raise NotImplementedError()
class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
files_dropped = pyqtSignal(object, object)
show_book_info = pyqtSignal()
resized = pyqtSignal(object)
def initialize(self, systray=None):
StatusBarInterface.initialize(self, systray=systray)
self.book_info = BookInfoDisplay(self.clear_message)
self.book_info.setAcceptDrops(True)
self.scroll_area = QScrollArea()
self.scroll_area.setWidget(self.book_info)
@ -192,15 +223,6 @@ class StatusBar(QStatusBar):
def reset_info(self):
self.book_info.show_data({})
def showMessage(self, msg, timeout=0):
ret = QStatusBar.showMessage(self, msg, timeout)
if self.notifier is not None and not config['disable_tray_notification']:
if isosx and isinstance(msg, unicode):
try:
msg = msg.encode(preferred_encoding)
except UnicodeEncodeError:
msg = msg.encode('utf-8')
self.notifier(msg)
return ret
def show_data(self, data):
self.book_info.show_data(data)

View File

@ -223,7 +223,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
####################### Setup device detection ########################
self.device_manager = DeviceManager(Dispatcher(self.device_detected),
self.job_manager, Dispatcher(self.status_bar.showMessage))
self.job_manager, Dispatcher(self.status_bar.show_message))
self.device_manager.start()
@ -256,8 +256,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
####################### Status Bar #####################
self.status_bar.initialize(self.system_tray_icon)
self.status_bar.show_book_info.connect(self.show_book_info)
self.status_bar.files_dropped.connect(self.files_dropped_on_book)
self.book_details.show_book_info.connect(self.show_book_info)
self.book_details.files_dropped.connect(self.files_dropped_on_book)
####################### Setup Toolbar #####################
ToolbarMixin.__init__(self)
@ -482,7 +482,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
Dispatcher(self.info_read))
self.set_default_thumbnail(\
self.device_manager.device.THUMBNAIL_HEIGHT)
self.status_bar.showMessage(_('Device: ')+\
self.status_bar.show_message(_('Device: ')+\
self.device_manager.device.__class__.get_gui_name()+\
_(' detected.'), 3000)
self.device_connected = 'device' if not is_folder_device else 'folder'
@ -503,7 +503,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
dict(version=self.latest_version, device=' '))
self.device_info = ' '
if self.current_view() != self.library_view:
self.status_bar.reset_info()
self.book_details.reset_info()
self.location_view.setCurrentIndex(self.location_view.model().index(0))
self.eject_action.setEnabled(False)
self.refresh_ondevice_info (device_connected = False)
@ -861,7 +861,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
to_device = allow_device and self.stack.currentIndex() != 0
self._add_books(books, to_device)
if to_device:
self.status_bar.showMessage(\
self.status_bar.show_message(\
_('Uploading books to device.'), 2000)
@ -912,7 +912,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
self.upload_books(paths,
list(map(ascii_filename, names)),
infos, on_card=on_card)
self.status_bar.showMessage(
self.status_bar.show_message(
_('Uploading books to device.'), 2000)
if getattr(self._adder, 'number_of_books_added', 0) > 0:
self.library_view.model().books_added(self._adder.number_of_books_added)
@ -1058,7 +1058,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
job = self.remove_paths(paths)
self.delete_memory[job] = (paths, view.model())
view.model().mark_for_deletion(job, rows)
self.status_bar.showMessage(_('Deleting books from device.'), 1000)
self.status_bar.show_message(_('Deleting books from device.'), 1000)
def remove_paths(self, paths):
return self.device_manager.delete_books(\
@ -1424,7 +1424,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
job.catalog_file_path = out
job.fmt = fmt
job.catalog_sync, job.catalog_title = sync, title
self.status_bar.showMessage(_('Generating %s catalog...')%fmt)
self.status_bar.show_message(_('Generating %s catalog...')%fmt)
def catalog_generated(self, job):
if job.result:
@ -1440,7 +1440,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
sync = dynamic.get('catalogs_to_be_synced', set([]))
sync.add(id)
dynamic.set('catalogs_to_be_synced', sync)
self.status_bar.showMessage(_('Catalog generated.'), 3000)
self.status_bar.show_message(_('Catalog generated.'), 3000)
self.sync_catalogs()
if job.fmt not in ['EPUB','MOBI']:
export_dir = choose_dir(self, _('Export Catalog Directory'),
@ -1458,7 +1458,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
Dispatcher(self.scheduled_recipe_fetched), func, args=args,
description=desc)
self.conversion_jobs[job] = (temp_files, fmt, arg)
self.status_bar.showMessage(_('Fetching news from ')+arg['title'], 2000)
self.status_bar.show_message(_('Fetching news from ')+arg['title'], 2000)
def scheduled_recipe_fetched(self, job):
temp_files, fmt, arg = self.conversion_jobs.pop(job)
@ -1472,7 +1472,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
sync.add(id)
dynamic.set('news_to_be_synced', sync)
self.scheduler.recipe_downloaded(arg)
self.status_bar.showMessage(arg['title'] + _(' fetched.'), 3000)
self.status_bar.show_message(arg['title'] + _(' fetched.'), 3000)
self.email_news(id)
self.sync_news()
@ -1552,7 +1552,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
num = len(jobs)
if num > 0:
self.status_bar.showMessage(_('Starting conversion of %d book(s)') %
self.status_bar.show_message(_('Starting conversion of %d book(s)') %
num, 2000)
def queue_convert_jobs(self, jobs, changed, bad, rows, previous,
@ -1599,7 +1599,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
self.library_view.model().db.add_format(book_id, \
fmt, data, index_is_id=True)
data.close()
self.status_bar.showMessage(job.description + \
self.status_bar.show_message(job.description + \
(' completed'), 2000)
finally:
for f in temp_files:
@ -1802,9 +1802,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
self.library_view.set_database(db)
self.tags_view.set_database(db, self.tag_match, self.popularity)
self.library_view.model().set_book_on_device_func(self.book_on_device)
self.status_bar.clearMessage()
self.status_bar.clear_message()
self.search.clear_to_help()
self.status_bar.reset_info()
self.book_details.reset_info()
self.library_view.model().count_changed()
prefs['library_path'] = self.library_path
@ -1831,7 +1831,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
'''
page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3
self.stack.setCurrentIndex(page)
self.status_bar.reset_info()
self.book_details.reset_info()
for x in ('tb', 'cb'):
splitter = getattr(self, x+'_splitter')
splitter.button.setEnabled(location == 'library')

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ class SafeLocalTimeZone(tzlocal):
def compute_locale_info_for_parse_date():
try:
dt = datetime.strptime('1/5/2000', "%x")
except ValueError:
except:
try:
dt = datetime.strptime('1/5/01', '%x')
except: