mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
ce9b91ef27
@ -105,7 +105,6 @@ function init_sort_combobox() {
|
|||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
$("#container").corner("30px");
|
$("#container").corner("30px");
|
||||||
$("#header").corner("30px");
|
$("#header").corner("30px");
|
||||||
|
@ -13,7 +13,8 @@ from calibre.devices.errors import UserFeedback
|
|||||||
from calibre.devices.usbms.deviceconfig import DeviceConfig
|
from calibre.devices.usbms.deviceconfig import DeviceConfig
|
||||||
from calibre.devices.interface import DevicePlugin
|
from calibre.devices.interface import DevicePlugin
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
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.book.base import Metadata
|
||||||
from calibre.ebooks.metadata.epub import set_metadata
|
from calibre.ebooks.metadata.epub import set_metadata
|
||||||
from calibre.library.server.utils import strftime
|
from calibre.library.server.utils import strftime
|
||||||
@ -96,6 +97,9 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
OPEN_FEEDBACK_MESSAGE = _(
|
OPEN_FEEDBACK_MESSAGE = _(
|
||||||
'Apple device detected, launching iTunes, please wait ...')
|
'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:
|
# Product IDs:
|
||||||
# 0x1291 iPod Touch
|
# 0x1291 iPod Touch
|
||||||
@ -3128,6 +3132,9 @@ class Book(Metadata):
|
|||||||
See ebooks.metadata.book.base
|
See ebooks.metadata.book.base
|
||||||
'''
|
'''
|
||||||
def __init__(self,title,author):
|
def __init__(self,title,author):
|
||||||
|
|
||||||
Metadata.__init__(self, title, authors=[author])
|
Metadata.__init__(self, title, authors=[author])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def title_sorter(self):
|
||||||
|
return title_sort(self.title)
|
||||||
|
|
||||||
|
@ -39,9 +39,9 @@ class DevicePlugin(Plugin):
|
|||||||
#: Whether the metadata on books can be set via the GUI.
|
#: Whether the metadata on books can be set via the GUI.
|
||||||
CAN_SET_METADATA = ['title', 'authors', 'collections']
|
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
|
# 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 separator for paths to books on device
|
||||||
path_sep = os.sep
|
path_sep = os.sep
|
||||||
|
@ -6,6 +6,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import os, re, time, sys
|
import os, re, time, sys
|
||||||
|
|
||||||
|
from calibre.ebooks.metadata import title_sort
|
||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
from calibre.devices.mime import mime_type_ext
|
from calibre.devices.mime import mime_type_ext
|
||||||
from calibre.devices.interface import BookList as _BookList
|
from calibre.devices.interface import BookList as _BookList
|
||||||
@ -54,7 +55,7 @@ class Book(Metadata):
|
|||||||
def title_sorter(self):
|
def title_sorter(self):
|
||||||
doc = '''String to sort the title. If absent, title is returned'''
|
doc = '''String to sort the title. If absent, title is returned'''
|
||||||
def fget(self):
|
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)
|
return property(doc=doc, fget=fget)
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
|
@ -96,7 +96,7 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
# USB disk-based devices can see the book files on the device, so can
|
# USB disk-based devices can see the book files on the device, so can
|
||||||
# copy these back to the library
|
# copy these back to the library
|
||||||
SUPPORTS_BACKLOADING = True
|
BACKLOADING_ERROR_MESSAGE = None
|
||||||
|
|
||||||
def reset(self, key='-1', log_packets=False, report_progress=None,
|
def reset(self, key='-1', log_packets=False, report_progress=None,
|
||||||
detected_device=None):
|
detected_device=None):
|
||||||
|
@ -235,6 +235,10 @@ class AddAction(InterfaceAction):
|
|||||||
self.gui.refresh_ondevice()
|
self.gui.refresh_ondevice()
|
||||||
|
|
||||||
def add_books_from_device(self, view, paths=None):
|
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:
|
if paths is None:
|
||||||
rows = view.selectionModel().selectedRows()
|
rows = view.selectionModel().selectedRows()
|
||||||
if not rows or len(rows) == 0:
|
if not rows or len(rows) == 0:
|
||||||
|
@ -19,12 +19,7 @@ class AddToLibraryAction(InterfaceAction):
|
|||||||
self.qaction.triggered.connect(self.add_books_to_library)
|
self.qaction.triggered.connect(self.add_books_to_library)
|
||||||
|
|
||||||
def location_selected(self, loc):
|
def location_selected(self, loc):
|
||||||
enabled = False
|
enabled = loc != 'library'
|
||||||
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)
|
|
||||||
self.qaction.setEnabled(enabled)
|
self.qaction.setEnabled(enabled)
|
||||||
|
|
||||||
def add_books_to_library(self, *args):
|
def add_books_to_library(self, *args):
|
||||||
|
@ -794,13 +794,16 @@ class DeviceMixin(object): # {{{
|
|||||||
mainlist, cardalist, cardblist = job.result
|
mainlist, cardalist, cardblist = job.result
|
||||||
self.memory_view.set_database(mainlist)
|
self.memory_view.set_database(mainlist)
|
||||||
self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA,
|
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_database(cardalist)
|
||||||
self.card_a_view.set_editable(self.device_manager.device.CAN_SET_METADATA,
|
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_database(cardblist)
|
||||||
self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA,
|
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_news()
|
||||||
self.sync_catalogs()
|
self.sync_catalogs()
|
||||||
self.refresh_ondevice()
|
self.refresh_ondevice()
|
||||||
|
@ -263,7 +263,7 @@
|
|||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>00</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
@ -357,13 +357,13 @@ from the value in the box</string>
|
|||||||
</item>
|
</item>
|
||||||
<item row="12" column="0" colspan="2">
|
<item row="12" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="change_title_to_title_case">
|
<widget class="QCheckBox" name="change_title_to_title_case">
|
||||||
<property name="text">
|
|
||||||
<string>Change title to title case</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Force the title to be in title case. If both this and swap authors are checked,
|
<string>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</string>
|
title and author are swapped before the title case is set</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Change title to title case</string>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="0" colspan="2">
|
<item row="10" column="0" colspan="2">
|
||||||
@ -486,15 +486,15 @@ Future conversion of these books will use the default settings.</string>
|
|||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="HistoryLineEdit" name="search_for">
|
<widget class="HistoryLineEdit" name="search_for">
|
||||||
<property name="toolTip">
|
|
||||||
<string>Enter the what you are looking for, either plain text or a regular expression, depending on the mode</string>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
<horstretch>100</horstretch>
|
<horstretch>100</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Enter the what you are looking for, either plain text or a regular expression, depending on the mode</string>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="2">
|
<item row="4" column="2">
|
||||||
@ -656,6 +656,14 @@ nothing should be put between the original text and the inserted text</string>
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="gridLayoutWidget_2">
|
<widget class="QWidget" name="gridLayoutWidget_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>122</width>
|
||||||
|
<height>34</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
<layout class="QGridLayout" name="testgrid">
|
<layout class="QGridLayout" name="testgrid">
|
||||||
<item row="8" column="0">
|
<item row="8" column="0">
|
||||||
<widget class="QLabel" name="label_31">
|
<widget class="QLabel" name="label_31">
|
||||||
@ -733,14 +741,33 @@ nothing should be put between the original text and the inserted text</string>
|
|||||||
<tabstop>author_sort</tabstop>
|
<tabstop>author_sort</tabstop>
|
||||||
<tabstop>rating</tabstop>
|
<tabstop>rating</tabstop>
|
||||||
<tabstop>publisher</tabstop>
|
<tabstop>publisher</tabstop>
|
||||||
<tabstop>tag_editor_button</tabstop>
|
|
||||||
<tabstop>tags</tabstop>
|
<tabstop>tags</tabstop>
|
||||||
|
<tabstop>tag_editor_button</tabstop>
|
||||||
<tabstop>remove_tags</tabstop>
|
<tabstop>remove_tags</tabstop>
|
||||||
|
<tabstop>remove_all_tags</tabstop>
|
||||||
<tabstop>series</tabstop>
|
<tabstop>series</tabstop>
|
||||||
|
<tabstop>clear_series</tabstop>
|
||||||
<tabstop>autonumber_series</tabstop>
|
<tabstop>autonumber_series</tabstop>
|
||||||
|
<tabstop>series_numbering_restarts</tabstop>
|
||||||
|
<tabstop>series_start_number</tabstop>
|
||||||
<tabstop>remove_format</tabstop>
|
<tabstop>remove_format</tabstop>
|
||||||
|
<tabstop>remove_conversion_settings</tabstop>
|
||||||
<tabstop>swap_title_and_author</tabstop>
|
<tabstop>swap_title_and_author</tabstop>
|
||||||
|
<tabstop>change_title_to_title_case</tabstop>
|
||||||
<tabstop>button_box</tabstop>
|
<tabstop>button_box</tabstop>
|
||||||
|
<tabstop>central_widget</tabstop>
|
||||||
|
<tabstop>search_field</tabstop>
|
||||||
|
<tabstop>search_mode</tabstop>
|
||||||
|
<tabstop>search_for</tabstop>
|
||||||
|
<tabstop>case_sensitive</tabstop>
|
||||||
|
<tabstop>replace_with</tabstop>
|
||||||
|
<tabstop>replace_func</tabstop>
|
||||||
|
<tabstop>destination_field</tabstop>
|
||||||
|
<tabstop>replace_mode</tabstop>
|
||||||
|
<tabstop>comma_separated</tabstop>
|
||||||
|
<tabstop>scrollArea11</tabstop>
|
||||||
|
<tabstop>test_text</tabstop>
|
||||||
|
<tabstop>test_result</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../resources/images.qrc"/>
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
@ -11,6 +11,7 @@ This module implements a simple commandline SMTP client that supports:
|
|||||||
|
|
||||||
import sys, traceback, os
|
import sys, traceback, os
|
||||||
from email import encoders
|
from email import encoders
|
||||||
|
from calibre import isbytestring
|
||||||
|
|
||||||
def create_mail(from_, to, subject, text=None, attachment_data=None,
|
def create_mail(from_, to, subject, text=None, attachment_data=None,
|
||||||
attachment_type=None, attachment_name=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:
|
if text is not None:
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
|
if isbytestring(text):
|
||||||
msg = MIMEText(text)
|
msg = MIMEText(text)
|
||||||
|
else:
|
||||||
|
msg = MIMEText(text, 'plain', 'utf-8')
|
||||||
outer.attach(msg)
|
outer.attach(msg)
|
||||||
|
|
||||||
if attachment_data is not None:
|
if attachment_data is not None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user