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
9dbe62c036
BIN
resources/images/devices/tablet.png
Normal file
BIN
resources/images/devices/tablet.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
@ -99,6 +99,12 @@ class DevicePlugin(Plugin):
|
|||||||
#: after the books lists have been loaded to get the driveinfo.
|
#: after the books lists have been loaded to get the driveinfo.
|
||||||
SLOW_DRIVEINFO = False
|
SLOW_DRIVEINFO = False
|
||||||
|
|
||||||
|
#: If set to True, calibre will ask the user if they want to manage the
|
||||||
|
#: device with calibre, the first time it is detected. If you set this to
|
||||||
|
#: True you must implement :meth:`get_device_uid()` and
|
||||||
|
#: :meth:`ignore_connected_device()`.
|
||||||
|
ASK_TO_ALLOW_CONNECT = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_gui_name(cls):
|
def get_gui_name(cls):
|
||||||
if hasattr(cls, 'gui_name'):
|
if hasattr(cls, 'gui_name'):
|
||||||
@ -587,6 +593,24 @@ class DevicePlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_device_uid(self):
|
||||||
|
'''
|
||||||
|
Must return a unique id for the currently connected device (this is
|
||||||
|
called immediately after a successful call to open()). You must
|
||||||
|
implement this method if you set ASK_TO_ALLOW_CONNECT = True
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def ignore_connected_device(self, uid):
|
||||||
|
'''
|
||||||
|
Should ignore the device identified by uid (the result of a call to
|
||||||
|
get_device_uid()) in the future. You must implement this method if you
|
||||||
|
set ASK_TO_ALLOW_CONNECT = True. Note that this function is called
|
||||||
|
immediately after open(), so if open() caches some state, the driver
|
||||||
|
should reset that state.
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
# Dynamic control interface.
|
# Dynamic control interface.
|
||||||
# The following methods are probably called on the GUI thread. Any driver
|
# The following methods are probably called on the GUI thread. Any driver
|
||||||
# that implements these methods must take pains to be thread safe, because
|
# that implements these methods must take pains to be thread safe, because
|
||||||
|
@ -27,7 +27,7 @@ def synchronous(func):
|
|||||||
class MTPDeviceBase(DevicePlugin):
|
class MTPDeviceBase(DevicePlugin):
|
||||||
name = 'MTP Device Interface'
|
name = 'MTP Device Interface'
|
||||||
gui_name = _('MTP Device')
|
gui_name = _('MTP Device')
|
||||||
icon = I('devices/galaxy_s3.png')
|
icon = I('devices/tablet.png')
|
||||||
description = _('Communicate with MTP devices')
|
description = _('Communicate with MTP devices')
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
version = (1, 0, 0)
|
version = (1, 0, 0)
|
||||||
|
@ -38,6 +38,7 @@ class MTP_DEVICE(BASE):
|
|||||||
FORMATS = ['epub', 'azw3', 'mobi', 'pdf']
|
FORMATS = ['epub', 'azw3', 'mobi', 'pdf']
|
||||||
DEVICE_PLUGBOARD_NAME = 'MTP_DEVICE'
|
DEVICE_PLUGBOARD_NAME = 'MTP_DEVICE'
|
||||||
SLOW_DRIVEINFO = True
|
SLOW_DRIVEINFO = True
|
||||||
|
ASK_TO_ALLOW_CONNECT = True
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
BASE.__init__(self, *args, **kwargs)
|
BASE.__init__(self, *args, **kwargs)
|
||||||
@ -90,6 +91,17 @@ class MTP_DEVICE(BASE):
|
|||||||
|
|
||||||
self.current_device_defaults = self.device_defaults(device, self)
|
self.current_device_defaults = self.device_defaults(device, self)
|
||||||
|
|
||||||
|
def get_device_uid(self):
|
||||||
|
return self.current_serial_num
|
||||||
|
|
||||||
|
def ignore_connected_device(self, uid):
|
||||||
|
bl = self.prefs['blacklist']
|
||||||
|
if uid not in bl:
|
||||||
|
bl.append(uid)
|
||||||
|
self.prefs['blacklist'] = bl
|
||||||
|
if self.is_mtp_device_connected:
|
||||||
|
self.eject()
|
||||||
|
|
||||||
# Device information {{{
|
# Device information {{{
|
||||||
def _update_drive_info(self, storage, location_code, name=None):
|
def _update_drive_info(self, storage, location_code, name=None):
|
||||||
import uuid
|
import uuid
|
||||||
|
@ -34,7 +34,7 @@ from calibre.library import current_library_name
|
|||||||
from calibre.library.server import server_config as content_server_config
|
from calibre.library.server import server_config as content_server_config
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.utils.ipc import eintr_retry_call
|
from calibre.utils.ipc import eintr_retry_call
|
||||||
from calibre.utils.config import from_json, tweaks, ConfigProxy
|
from calibre.utils.config import from_json, tweaks
|
||||||
from calibre.utils.date import isoformat, now
|
from calibre.utils.date import isoformat, now
|
||||||
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
|
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
|
||||||
from calibre.utils.mdns import (publish as publish_zeroconf, unpublish as
|
from calibre.utils.mdns import (publish as publish_zeroconf, unpublish as
|
||||||
@ -171,7 +171,7 @@ class SDBook(Book):
|
|||||||
|
|
||||||
class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
||||||
name = 'SmartDevice App Interface'
|
name = 'SmartDevice App Interface'
|
||||||
gui_name = _('Wireless Device')
|
gui_name = gui_name_base = _('Wireless Device')
|
||||||
gui_name_template = '%s: %s'
|
gui_name_template = '%s: %s'
|
||||||
|
|
||||||
icon = I('devices/galaxy_s3.png')
|
icon = I('devices/galaxy_s3.png')
|
||||||
@ -1056,6 +1056,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
def eject(self):
|
def eject(self):
|
||||||
self._debug()
|
self._debug()
|
||||||
self._close_device_socket()
|
self._close_device_socket()
|
||||||
|
self.gui_name = self.gui_name_base
|
||||||
|
|
||||||
@synchronous('sync_lock')
|
@synchronous('sync_lock')
|
||||||
def post_yank_cleanup(self):
|
def post_yank_cleanup(self):
|
||||||
|
@ -294,7 +294,9 @@ def question_dialog(parent, title, msg, det_msg='', show_copy_button=False,
|
|||||||
# Set skip_dialog_name to a unique name for this dialog
|
# Set skip_dialog_name to a unique name for this dialog
|
||||||
# Set skip_dialog_msg to a message displayed to the user
|
# Set skip_dialog_msg to a message displayed to the user
|
||||||
skip_dialog_name=None, skip_dialog_msg=_('Show this confirmation again'),
|
skip_dialog_name=None, skip_dialog_msg=_('Show this confirmation again'),
|
||||||
skip_dialog_skipped_value=True, skip_dialog_skip_precheck=True):
|
skip_dialog_skipped_value=True, skip_dialog_skip_precheck=True,
|
||||||
|
# Override icon (QIcon to be used as the icon for this dialog)
|
||||||
|
override_icon=None):
|
||||||
from calibre.gui2.dialogs.message_box import MessageBox
|
from calibre.gui2.dialogs.message_box import MessageBox
|
||||||
|
|
||||||
auto_skip = set(gprefs.get('questions_to_auto_skip', []))
|
auto_skip = set(gprefs.get('questions_to_auto_skip', []))
|
||||||
@ -302,7 +304,8 @@ def question_dialog(parent, title, msg, det_msg='', show_copy_button=False,
|
|||||||
return bool(skip_dialog_skipped_value)
|
return bool(skip_dialog_skipped_value)
|
||||||
|
|
||||||
d = MessageBox(MessageBox.QUESTION, title, msg, det_msg, parent=parent,
|
d = MessageBox(MessageBox.QUESTION, title, msg, det_msg, parent=parent,
|
||||||
show_copy_button=show_copy_button, default_yes=default_yes)
|
show_copy_button=show_copy_button, default_yes=default_yes,
|
||||||
|
q_icon=override_icon)
|
||||||
|
|
||||||
if skip_dialog_name is not None and skip_dialog_msg:
|
if skip_dialog_name is not None and skip_dialog_msg:
|
||||||
tc = d.toggle_checkbox
|
tc = d.toggle_checkbox
|
||||||
|
@ -20,7 +20,7 @@ from calibre.utils.ipc.job import BaseJob
|
|||||||
from calibre.devices.scanner import DeviceScanner
|
from calibre.devices.scanner import DeviceScanner
|
||||||
from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic,
|
from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic,
|
||||||
warning_dialog, info_dialog, choose_dir, FunctionDispatcher,
|
warning_dialog, info_dialog, choose_dir, FunctionDispatcher,
|
||||||
show_restart_warning)
|
show_restart_warning, gprefs, question_dialog)
|
||||||
from calibre.ebooks.metadata import authors_to_string
|
from calibre.ebooks.metadata import authors_to_string
|
||||||
from calibre import preferred_encoding, prints, force_unicode, as_unicode
|
from calibre import preferred_encoding, prints, force_unicode, as_unicode
|
||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename
|
||||||
@ -122,7 +122,7 @@ def device_name_for_plugboards(device_class):
|
|||||||
class DeviceManager(Thread): # {{{
|
class DeviceManager(Thread): # {{{
|
||||||
|
|
||||||
def __init__(self, connected_slot, job_manager, open_feedback_slot,
|
def __init__(self, connected_slot, job_manager, open_feedback_slot,
|
||||||
open_feedback_msg, sleep_time=2):
|
open_feedback_msg, allow_connect_slot, sleep_time=2):
|
||||||
'''
|
'''
|
||||||
:sleep_time: Time to sleep between device probes in secs
|
:sleep_time: Time to sleep between device probes in secs
|
||||||
'''
|
'''
|
||||||
@ -136,6 +136,7 @@ class DeviceManager(Thread): # {{{
|
|||||||
x.MANAGES_DEVICE_PRESENCE]
|
x.MANAGES_DEVICE_PRESENCE]
|
||||||
self.sleep_time = sleep_time
|
self.sleep_time = sleep_time
|
||||||
self.connected_slot = connected_slot
|
self.connected_slot = connected_slot
|
||||||
|
self.allow_connect_slot = allow_connect_slot
|
||||||
self.jobs = Queue.Queue(0)
|
self.jobs = Queue.Queue(0)
|
||||||
self.job_steps = Queue.Queue(0)
|
self.job_steps = Queue.Queue(0)
|
||||||
self.keep_going = True
|
self.keep_going = True
|
||||||
@ -193,6 +194,21 @@ class DeviceManager(Thread): # {{{
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def after_device_connect(self, dev, device_kind):
|
def after_device_connect(self, dev, device_kind):
|
||||||
|
allow_connect = True
|
||||||
|
try:
|
||||||
|
uid = dev.get_device_uid()
|
||||||
|
except NotImplementedError:
|
||||||
|
uid = None
|
||||||
|
asked = gprefs.get('ask_to_manage_device', [])
|
||||||
|
if (dev.ASK_TO_ALLOW_CONNECT and uid and uid not in asked):
|
||||||
|
if not self.allow_connect_slot(dev.get_gui_name(), dev.icon):
|
||||||
|
allow_connect = False
|
||||||
|
asked.append(uid)
|
||||||
|
gprefs.set('ask_to_manage_device', asked)
|
||||||
|
if not allow_connect:
|
||||||
|
dev.ignore_connected_device(uid)
|
||||||
|
return
|
||||||
|
|
||||||
self.connected_device = dev
|
self.connected_device = dev
|
||||||
self.connected_device_kind = device_kind
|
self.connected_device_kind = device_kind
|
||||||
self.connected_slot(True, device_kind)
|
self.connected_slot(True, device_kind)
|
||||||
@ -829,12 +845,19 @@ class DeviceMixin(object): # {{{
|
|||||||
self.device_error_dialog.setModal(Qt.NonModal)
|
self.device_error_dialog.setModal(Qt.NonModal)
|
||||||
self.device_manager = DeviceManager(FunctionDispatcher(self.device_detected),
|
self.device_manager = DeviceManager(FunctionDispatcher(self.device_detected),
|
||||||
self.job_manager, Dispatcher(self.status_bar.show_message),
|
self.job_manager, Dispatcher(self.status_bar.show_message),
|
||||||
Dispatcher(self.show_open_feedback))
|
Dispatcher(self.show_open_feedback),
|
||||||
|
FunctionDispatcher(self.allow_connect))
|
||||||
self.device_manager.start()
|
self.device_manager.start()
|
||||||
self.device_manager.devices_initialized.wait()
|
self.device_manager.devices_initialized.wait()
|
||||||
if tweaks['auto_connect_to_folder']:
|
if tweaks['auto_connect_to_folder']:
|
||||||
self.connect_to_folder_named(tweaks['auto_connect_to_folder'])
|
self.connect_to_folder_named(tweaks['auto_connect_to_folder'])
|
||||||
|
|
||||||
|
def allow_connect(self, name, icon):
|
||||||
|
return question_dialog(self, _('Mange the %s?')%name,
|
||||||
|
_('Detected the <b>%s</b>. Do you want calibre to manage it?')%
|
||||||
|
name, show_copy_button=False,
|
||||||
|
override_icon=QIcon(icon))
|
||||||
|
|
||||||
def debug_detection(self, done):
|
def debug_detection(self, done):
|
||||||
self.debug_detection_callback = weakref.ref(done)
|
self.debug_detection_callback = weakref.ref(done)
|
||||||
self.device_manager.debug_detection(FunctionDispatcher(self.debug_detection_done))
|
self.device_manager.debug_detection(FunctionDispatcher(self.debug_detection_done))
|
||||||
|
@ -415,6 +415,7 @@ class MTPConfig(QTabWidget):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
|
self.device.prefs['blacklist'] = self.igntab.blacklist
|
||||||
p = self.device.prefs.get(self.current_device_key, {})
|
p = self.device.prefs.get(self.current_device_key, {})
|
||||||
|
|
||||||
if hasattr(self, 'formats'):
|
if hasattr(self, 'formats'):
|
||||||
@ -440,7 +441,6 @@ class MTPConfig(QTabWidget):
|
|||||||
|
|
||||||
self.device.prefs[self.current_device_key] = p
|
self.device.prefs[self.current_device_key] = p
|
||||||
|
|
||||||
self.device.prefs['blacklist'] = self.igntab.blacklist
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from calibre.gui2 import Application
|
from calibre.gui2 import Application
|
||||||
|
Loading…
x
Reference in New Issue
Block a user