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: