diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 20b85df915..e2a8dc934a 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -105,7 +105,6 @@ function init_sort_combobox() { // }}} - function init() { $("#container").corner("30px"); $("#header").corner("30px"); diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 9465b789ae..9ad3cf3e08 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -13,7 +13,8 @@ from calibre.devices.errors import UserFeedback from calibre.devices.usbms.deviceconfig import DeviceConfig from calibre.devices.interface import DevicePlugin from calibre.ebooks.BeautifulSoup import BeautifulSoup -from calibre.ebooks.metadata import authors_to_string, MetaInformation +from calibre.ebooks.metadata import authors_to_string, MetaInformation, \ + title_sort from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.epub import set_metadata from calibre.library.server.utils import strftime @@ -96,6 +97,9 @@ class ITUNES(DriverBase): OPEN_FEEDBACK_MESSAGE = _( 'Apple device detected, launching iTunes, please wait ...') + BACKLOADING_ERROR_MESSAGE = _( + "Cannot copy books directly from iDevice. " + "Drag from iTunes Library to desktop, then add to calibre's Library window.") # Product IDs: # 0x1291 iPod Touch @@ -3128,6 +3132,9 @@ class Book(Metadata): See ebooks.metadata.book.base ''' def __init__(self,title,author): - Metadata.__init__(self, title, authors=[author]) + @property + def title_sorter(self): + return title_sort(self.title) + diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 9c10de7d6c..75453c74b9 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -39,9 +39,9 @@ class DevicePlugin(Plugin): #: Whether the metadata on books can be set via the GUI. CAN_SET_METADATA = ['title', 'authors', 'collections'] - # Set this to True if the books on the device are files that the GUI can + # Set this to None if the books on the device are files that the GUI can # access in order to add the books from the device to the library - SUPPORTS_BACKLOADING = False + BACKLOADING_ERROR_MESSAGE = _('Cannot get files from this device') #: Path separator for paths to books on device path_sep = os.sep diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index 30d25781cf..462d78b233 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -6,6 +6,7 @@ __docformat__ = 'restructuredtext en' import os, re, time, sys +from calibre.ebooks.metadata import title_sort from calibre.ebooks.metadata.book.base import Metadata from calibre.devices.mime import mime_type_ext from calibre.devices.interface import BookList as _BookList @@ -54,7 +55,7 @@ class Book(Metadata): def title_sorter(self): doc = '''String to sort the title. If absent, title is returned''' def fget(self): - return re.sub('^\s*A\s+|^\s*The\s+|^\s*An\s+', '', self.title).rstrip() + return title_sort(self.title) return property(doc=doc, fget=fget) @dynamic_property diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index cb26f6a50c..6fcfb9e7f0 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -96,7 +96,7 @@ class Device(DeviceConfig, DevicePlugin): # USB disk-based devices can see the book files on the device, so can # copy these back to the library - SUPPORTS_BACKLOADING = True + BACKLOADING_ERROR_MESSAGE = None def reset(self, key='-1', log_packets=False, report_progress=None, detected_device=None): diff --git a/src/calibre/gui2/actions/add.py b/src/calibre/gui2/actions/add.py index be1f8f4eaf..9eb197984e 100644 --- a/src/calibre/gui2/actions/add.py +++ b/src/calibre/gui2/actions/add.py @@ -235,6 +235,10 @@ class AddAction(InterfaceAction): self.gui.refresh_ondevice() def add_books_from_device(self, view, paths=None): + backloading_err = self.gui.device_manager.device.BACKLOADING_ERROR_MESSAGE + if backloading_err is not None: + return error_dialog(self.gui, _('Add to library'), backloading_err, + show=True) if paths is None: rows = view.selectionModel().selectedRows() if not rows or len(rows) == 0: diff --git a/src/calibre/gui2/actions/add_to_library.py b/src/calibre/gui2/actions/add_to_library.py index efdf645acb..05aea8f1dd 100644 --- a/src/calibre/gui2/actions/add_to_library.py +++ b/src/calibre/gui2/actions/add_to_library.py @@ -19,12 +19,7 @@ class AddToLibraryAction(InterfaceAction): self.qaction.triggered.connect(self.add_books_to_library) def location_selected(self, loc): - enabled = False - if loc != 'library': - if self.gui.device_manager.is_device_connected: - device = self.gui.device_manager.connected_device - if device is not None: - enabled = getattr(device, 'SUPPORTS_BACKLOADING', False) + enabled = loc != 'library' self.qaction.setEnabled(enabled) def add_books_to_library(self, *args): diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 49bc5c0016..e662c6a5cc 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -794,13 +794,16 @@ class DeviceMixin(object): # {{{ mainlist, cardalist, cardblist = job.result self.memory_view.set_database(mainlist) self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA, - self.device_manager.device.SUPPORTS_BACKLOADING) + self.device_manager.device.BACKLOADING_ERROR_MESSAGE + is None) self.card_a_view.set_database(cardalist) self.card_a_view.set_editable(self.device_manager.device.CAN_SET_METADATA, - self.device_manager.device.SUPPORTS_BACKLOADING) + self.device_manager.device.BACKLOADING_ERROR_MESSAGE + is None) self.card_b_view.set_database(cardblist) self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA, - self.device_manager.device.SUPPORTS_BACKLOADING) + self.device_manager.device.BACKLOADING_ERROR_MESSAGE + is None) self.sync_news() self.sync_catalogs() self.refresh_ondevice() diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 60e24dbceb..3897d6dbf9 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -263,7 +263,7 @@ 20 - 00 + 0 @@ -357,13 +357,13 @@ from the value in the box - - Change title to title case - Force the title to be in title case. If both this and swap authors are checked, title and author are swapped before the title case is set + + Change title to title case + @@ -486,15 +486,15 @@ Future conversion of these books will use the default settings. - - Enter the what you are looking for, either plain text or a regular expression, depending on the mode - 100 0 + + Enter the what you are looking for, either plain text or a regular expression, depending on the mode + @@ -656,6 +656,14 @@ nothing should be put between the original text and the inserted text true + + + 0 + 0 + 122 + 34 + + @@ -733,14 +741,33 @@ nothing should be put between the original text and the inserted text author_sort rating publisher - tag_editor_button tags + tag_editor_button remove_tags + remove_all_tags series + clear_series autonumber_series + series_numbering_restarts + series_start_number remove_format + remove_conversion_settings swap_title_and_author + change_title_to_title_case button_box + central_widget + search_field + search_mode + search_for + case_sensitive + replace_with + replace_func + destination_field + replace_mode + comma_separated + scrollArea11 + test_text + test_result diff --git a/src/calibre/utils/smtp.py b/src/calibre/utils/smtp.py index 230a983b74..b8b46a96cb 100644 --- a/src/calibre/utils/smtp.py +++ b/src/calibre/utils/smtp.py @@ -11,6 +11,7 @@ This module implements a simple commandline SMTP client that supports: import sys, traceback, os from email import encoders +from calibre import isbytestring def create_mail(from_, to, subject, text=None, attachment_data=None, attachment_type=None, attachment_name=None): @@ -26,7 +27,10 @@ def create_mail(from_, to, subject, text=None, attachment_data=None, if text is not None: from email.mime.text import MIMEText - msg = MIMEText(text) + if isbytestring(text): + msg = MIMEText(text) + else: + msg = MIMEText(text, 'plain', 'utf-8') outer.attach(msg) if attachment_data is not None: