Pull from trunk

This commit is contained in:
Kovid Goyal 2010-09-15 14:08:03 -06:00
commit 6e16d403bf
24 changed files with 94 additions and 25 deletions

View File

@ -35,7 +35,7 @@ class PRS505(USBMS):
VENDOR_NAME = 'SONY'
WINDOWS_MAIN_MEM = re.compile(
r'(PRS-(505|300|500))|'
r'(PRS-(505|500))|'
r'(PRS-((700[#/])|((6|9|3)(0|5)0&)))'
)
WINDOWS_CARD_A_MEM = re.compile(

View File

@ -50,6 +50,7 @@ gprefs.defaults['action-layout-context-menu-device'] = (
gprefs.defaults['show_splash_screen'] = True
gprefs.defaults['toolbar_icon_size'] = 'medium'
gprefs.defaults['toolbar_text'] = 'auto'
gprefs.defaults['show_child_bar'] = False
# }}}

View File

@ -71,6 +71,12 @@ class InterfaceAction(QObject):
all_locations = frozenset(['toolbar', 'toolbar-device', 'context-menu',
'context-menu-device'])
#: Type of action
#: 'current' means acts on the current view
#: 'global' means an action that does not act on the current view, but rather
#: on calibre as a whole
action_type = 'global'
def __init__(self, parent, site_customization):
QObject.__init__(self, parent)
self.setObjectName(self.name)

View File

@ -25,6 +25,7 @@ class AddAction(InterfaceAction):
action_spec = (_('Add books'), 'add_book.png',
_('Add books to the calibre library/device from files on your computer')
, _('A'))
action_type = 'current'
def genesis(self):
self._add_filesystem_book = self.Dispatcher(self.__add_filesystem_book)

View File

@ -13,6 +13,7 @@ class AddToLibraryAction(InterfaceAction):
action_spec = (_('Add books to library'), 'add_book.png',
_('Add books to your calibre library from the connected device'), None)
dont_add_to = frozenset(['toolbar', 'context-menu'])
action_type = 'current'
def genesis(self):
self.qaction.triggered.connect(self.add_books_to_library)

View File

@ -18,6 +18,7 @@ class FetchAnnotationsAction(InterfaceAction):
name = 'Fetch Annotations'
action_spec = (_('Fetch annotations (experimental)'), None, None, None)
action_type = 'current'
def genesis(self):
pass

View File

@ -21,6 +21,7 @@ class ConvertAction(InterfaceAction):
name = 'Convert Books'
action_spec = (_('Convert books'), 'convert.png', None, _('C'))
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
action_type = 'current'
def genesis(self):
cm = QMenu()

View File

@ -80,6 +80,7 @@ class CopyToLibraryAction(InterfaceAction):
_('Copy selected books to the specified library'), None)
popup_type = QToolButton.InstantPopup
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
action_type = 'current'
def genesis(self):
self.menu = QMenu(self.gui)

View File

@ -16,6 +16,7 @@ class DeleteAction(InterfaceAction):
name = 'Remove Books'
action_spec = (_('Remove books'), 'trash.png', None, _('Del'))
action_type = 'current'
def genesis(self):
self.qaction.triggered.connect(self.delete_books)

View File

@ -13,6 +13,7 @@ class EditCollectionsAction(InterfaceAction):
action_spec = (_('Manage collections'), None,
_('Manage the collections on this device'), None)
dont_add_to = frozenset(['toolbar', 'context-menu'])
action_type = 'current'
def genesis(self):
self.qaction.triggered.connect(self.edit_collections)

View File

@ -22,6 +22,7 @@ class EditMetadataAction(InterfaceAction):
name = 'Edit Metadata'
action_spec = (_('Edit metadata'), 'edit_input.png', None, _('E'))
action_type = 'current'
def genesis(self):
self.create_action(spec=(_('Merge book records'), 'merge_books.png',

View File

@ -14,6 +14,7 @@ class OpenFolderAction(InterfaceAction):
action_spec = (_('Open containing folder'), 'document_open.png', None,
_('O'))
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
action_type = 'current'
def genesis(self):
self.qaction.triggered.connect(self.gui.iactions['View'].view_folder)

View File

@ -38,6 +38,7 @@ class SaveToDiskAction(InterfaceAction):
name = "Save To Disk"
action_spec = (_('Save to disk'), 'save.png', None, _('S'))
action_type = 'current'
def genesis(self):
self.qaction.triggered.connect(self.save_to_disk)

View File

@ -16,6 +16,7 @@ class ShowBookDetailsAction(InterfaceAction):
action_spec = (_('Show book details'), 'dialog_information.png', None,
_('I'))
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
action_type = 'current'
def genesis(self):
self.qaction.triggered.connect(self.show_book_info)

View File

@ -16,6 +16,7 @@ class SimilarBooksAction(InterfaceAction):
name = 'Similar Books'
action_spec = (_('Similar books...'), None, None, None)
popup_type = QToolButton.InstantPopup
action_type = 'current'
def genesis(self):
m = QMenu(self.gui)

View File

@ -22,6 +22,7 @@ class ViewAction(InterfaceAction):
name = 'View'
action_spec = (_('View'), 'view.png', None, _('V'))
action_type = 'current'
def genesis(self):
self.persistent_files = []

View File

@ -627,12 +627,11 @@ class DeviceMixin(object): # {{{
def connect_to_folder(self):
dir = choose_dir(self, 'Select Device Folder',
_('Select folder to open as device'))
kls = FOLDER_DEVICE
self.device_manager.mount_device(kls=kls, kind='folder', path=dir)
if dir is not None:
self.device_manager.mount_device(kls=FOLDER_DEVICE, kind='folder', path=dir)
def connect_to_itunes(self):
kls = ITUNES_ASYNC
self.device_manager.mount_device(kls=kls, kind='itunes', path=None)
self.device_manager.mount_device(kls=ITUNES_ASYNC, kind='itunes', path=None)
# disconnect from both folder and itunes devices
def disconnect_mounted_device(self):

View File

@ -61,7 +61,7 @@ class LocationManager(QObject): # {{{
ac('library', _('Library'), 'lt.png',
_('Show books in calibre library'))
ac('main', _('Reader'), 'reader.png',
ac('main', _('Device'), 'reader.png',
_('Show books in the main memory of the device'))
ac('carda', _('Card A'), 'sd.png',
_('Show books in storage card A'))
@ -197,11 +197,21 @@ class SearchBar(QWidget): # {{{
# }}}
class Spacer(QWidget):
def __init__(self, parent):
QWidget.__init__(self, parent)
self.l = QHBoxLayout()
self.setLayout(self.l)
self.l.addStretch(10)
class ToolBar(QToolBar): # {{{
def __init__(self, donate, location_manager, parent):
def __init__(self, donate, location_manager, child_bar, parent):
QToolBar.__init__(self, parent)
self.gui = parent
self.child_bar = child_bar
self.setContextMenuPolicy(Qt.PreventContextMenu)
self.setMovable(False)
self.setFloatable(False)
@ -223,16 +233,19 @@ class ToolBar(QToolBar): # {{{
sz = gprefs['toolbar_icon_size']
sz = {'small':24, 'medium':48, 'large':64}[sz]
self.setIconSize(QSize(sz, sz))
self.child_bar.setIconSize(QSize(sz, sz))
style = Qt.ToolButtonTextUnderIcon
if gprefs['toolbar_text'] == 'never':
style = Qt.ToolButtonIconOnly
self.setToolButtonStyle(style)
self.child_bar.setToolButtonStyle(style)
self.donate_button.set_normal_icon_size(sz, sz)
def contextMenuEvent(self, *args):
pass
def build_bar(self):
self.child_bar.setVisible(gprefs['show_child_bar'])
self.showing_donate = False
showing_device = self.location_manager.has_device
actions = '-device' if showing_device else ''
@ -244,10 +257,16 @@ class ToolBar(QToolBar): # {{{
m.setVisible(False)
self.clear()
self.child_bar.clear()
self.added_actions = []
self.spacers = [Spacer(self.child_bar), Spacer(self.child_bar),
Spacer(self), Spacer(self)]
self.child_bar.addWidget(self.spacers[0])
if gprefs['show_child_bar']:
self.addWidget(self.spacers[2])
for what in actions:
if what is None:
if what is None and not gprefs['show_child_bar']:
self.addSeparator()
elif what == 'Location Manager':
for ac in self.location_manager.available_actions:
@ -262,12 +281,21 @@ class ToolBar(QToolBar): # {{{
self.showing_donate = True
elif what in self.gui.iactions:
action = self.gui.iactions[what]
self.addAction(action.qaction)
bar = self
if action.action_type == 'current' and gprefs['show_child_bar']:
bar = self.child_bar
bar.addAction(action.qaction)
self.added_actions.append(action.qaction)
self.setup_tool_button(action.qaction, action.popup_type)
self.child_bar.addWidget(self.spacers[1])
if gprefs['show_child_bar']:
self.addWidget(self.spacers[3])
def setup_tool_button(self, ac, menu_mode=None):
ch = self.widgetForAction(ac)
if ch is None:
ch = self.child_bar.widgetForAction(ac)
ch.setCursor(Qt.PointingHandCursor)
ch.setAutoRaise(True)
if ac.menu() is not None and menu_mode is not None:
@ -280,7 +308,8 @@ class ToolBar(QToolBar): # {{{
if p == 'never':
style = Qt.ToolButtonIconOnly
if p == 'auto' and self.preferred_width > self.width()+35:
if p == 'auto' and self.preferred_width > self.width()+35 and \
not gprefs['show_child_bar']:
style = Qt.ToolButtonIconOnly
self.setToolButtonStyle(style)
@ -309,9 +338,11 @@ class MainWindowMixin(object): # {{{
self.iactions['Fetch News'].init_scheduler(db)
self.search_bar = SearchBar(self)
self.child_bar = QToolBar(self)
self.tool_bar = ToolBar(self.donate_button,
self.location_manager, self)
self.location_manager, self.child_bar, self)
self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
self.addToolBar(Qt.BottomToolBarArea, self.child_bar)
l = self.centralwidget.layout()
l.addWidget(self.search_bar)

View File

@ -46,6 +46,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('use_roman_numerals_for_series_number', config)
r('separate_cover_flow', config, restart_required=True)
r('search_as_you_type', config)
r('show_child_bar', gprefs)
choices = [(_('Small'), 'small'), (_('Medium'), 'medium'),
(_('Large'), 'large')]

View File

@ -173,6 +173,13 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="opt_show_child_bar">
<property name="text">
<string>&amp;Split the toolbar into two toolbars</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -605,7 +605,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def has_cover(self, index, index_is_id=False):
id = index if index_is_id else self.id(index)
path = os.path.join(self.library_path, self.path(id, index_is_id=True), 'cover.jpg')
try:
path = os.path.join(self.abspath(id, index_is_id=True), 'cover.jpg')
except:
# Can happen if path has not yet been set
return False
return os.access(path, os.R_OK)
def remove_cover(self, id, notify=True):
@ -616,6 +620,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
except (IOError, OSError):
time.sleep(0.2)
os.remove(path)
self.data.set(id, self.FIELD_MAP['cover'], False, row_is_id=True)
if notify:
self.notify('cover', [id])
@ -636,6 +641,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
except (IOError, OSError):
time.sleep(0.2)
save_cover_data_to(data, path)
self.data.set(id, self.FIELD_MAP['cover'], True, row_is_id=True)
if notify:
self.notify('cover', [id])
@ -1103,8 +1109,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
label=user_mi[key]['label'])
self.notify('metadata', [id])
# Given a book, return the list of author sort strings for the book's authors
def authors_sort_strings(self, id, index_is_id=False):
'''
Given a book, return the list of author sort strings
for the book's authors
'''
id = id if index_is_id else self.id(id)
aut_strings = self.conn.get('''
SELECT sort
@ -1773,10 +1782,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
series_index = 1.0 if mi.series_index is None else mi.series_index
aus = mi.author_sort if mi.author_sort else self.author_sort_from_authors(mi.authors)
title = mi.title
if isinstance(aus, str):
if isbytestring(aus):
aus = aus.decode(preferred_encoding, 'replace')
if isinstance(title, str):
title = title.decode(preferred_encoding)
if isbytestring(title):
title = title.decode(preferred_encoding, 'replace')
obj = self.conn.execute('INSERT INTO books(title, series_index, author_sort) VALUES (?, ?, ?)',
(title, series_index, aus))
id = obj.lastrowid

View File

@ -194,7 +194,7 @@ class Image(_magick.Image): # {{{
# }}}
def create_canvas(width, height, bgcolor='white'):
def create_canvas(width, height, bgcolor='#ffffff'):
canvas = Image()
canvas.create_canvas(int(width), int(height), str(bgcolor))
return canvas

View File

@ -11,7 +11,7 @@ from calibre.utils.magick import Image, DrawingWand, create_canvas
from calibre.constants import __appname__, __version__
from calibre import fit_image
def save_cover_data_to(data, path, bgcolor='white', resize_to=None,
def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None,
return_data=False):
'''
Saves image in data to path, in the format specified by the path
@ -28,7 +28,7 @@ def save_cover_data_to(data, path, bgcolor='white', resize_to=None,
return canvas.export(os.path.splitext(path)[1][1:])
canvas.save(path)
def thumbnail(data, width=120, height=120, bgcolor='white', fmt='jpg'):
def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg'):
img = Image()
img.load(data)
owidth, oheight = img.size
@ -61,7 +61,7 @@ def identify(path):
return identify_data(data)
def add_borders_to_image(path_to_image, left=0, top=0, right=0, bottom=0,
border_color='white'):
border_color='#ffffff'):
img = Image()
img.open(path_to_image)
lwidth, lheight = img.size
@ -80,7 +80,7 @@ def create_text_wand(font_size, font_path=None):
ans.text_alias = True
return ans
def create_text_arc(text, font_size, font=None, bgcolor='white'):
def create_text_arc(text, font_size, font=None, bgcolor='#ffffff'):
if isinstance(text, unicode):
text = text.encode('utf-8')
@ -148,7 +148,7 @@ class TextLine(object):
def create_cover_page(top_lines, logo_path, width=590, height=750,
bgcolor='white', output_format='jpg'):
bgcolor='#ffffff', output_format='jpg'):
'''
Create the standard calibre cover page and return it as a byte string in
the specified output_format.

View File

@ -290,10 +290,12 @@ class BasicNewsRecipe(Recipe):
#: the cover for the periodical. Overriding this in your recipe instructs
#: calibre to render the downloaded cover into a frame whose width and height
#: are expressed as a percentage of the downloaded cover.
#: cover_margins = (10,15,'white') pads the cover with a white margin
#: cover_margins = (10, 15, '#ffffff') pads the cover with a white margin
#: 10px on the left and right, 15px on the top and bottom.
#: Colors name defined at http://www.imagemagick.org/script/color.php
cover_margins = (0,0,'white')
#: Color names defined at http://www.imagemagick.org/script/color.php
#: Note that for some reason, white does not always work on windows. Use
#: #ffffff instead
cover_margins = (0, 0, '#ffffff')
#: Set to a non empty string to disable this recipe
#: The string will be used as the disabled message