diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 36bcbdbfe2..776b04d5f6 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -595,6 +595,7 @@ from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX from calibre.devices.nook.driver import NOOK, NOOK_COLOR from calibre.devices.prs505.driver import PRS505 +from calibre.devices.user_defined.driver import USER_DEFINED from calibre.devices.android.driver import ANDROID, S60 from calibre.devices.nokia.driver import N770, N810, E71X, E52 from calibre.devices.eslick.driver import ESLICK, EBK52 @@ -742,6 +743,7 @@ plugins += [ EEEREADER, NEXTBOOK, ITUNES, + USER_DEFINED, ] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ x.__name__.endswith('MetadataReader')] diff --git a/src/calibre/devices/__init__.py b/src/calibre/devices/__init__.py index 63b0b89a17..e47cd82b50 100644 --- a/src/calibre/devices/__init__.py +++ b/src/calibre/devices/__init__.py @@ -156,3 +156,60 @@ def debug(ioreg_to_tmp=False, buf=None): sys.stdout = oldo sys.stderr = olde +def device_info(ioreg_to_tmp=False, buf=None): + from calibre.devices.scanner import DeviceScanner, win_pnp_drives + from calibre.constants import iswindows + import re + + res = {} + device_details = {} + device_set = set() + drive_details = {} + drive_set = set() + res['device_set'] = device_set + res['device_details'] = device_details + res['drive_details'] = drive_details + res['drive_set'] = drive_set + + try: + s = DeviceScanner() + s.scan() + devices = (s.devices) + if not iswindows: + devices = [list(x) for x in devices] + for dev in devices: + for i in range(3): + dev[i] = hex(dev[i]) + d = dev[0] + dev[1] + dev[2] + device_set.add(d) + device_details[d] = dev[0:3] + else: + for dev in devices: + vid = re.search('vid_([0-9a-f]*)&', dev) + if vid: + vid = vid.group(1) + pid = re.search('pid_([0-9a-f]*)&', dev) + if pid: + pid = pid.group(1) + rev = re.search('rev_([0-9a-f]*)$', dev) + if rev: + rev = rev.group(1) + d = vid+pid+rev + device_set.add(d) + device_details[d] = (vid, pid, rev) + + drives = win_pnp_drives(debug=False) + for drive,details in drives.iteritems(): + order = 'ORD_' + str(drive.order) + ven = re.search('VEN_([^&]*)&', details) + if ven: + ven = ven.group(1) + prod = re.search('PROD_([^&]*)&', details) + if prod: + prod = prod.group(1) + d = (order, ven, prod) + drive_details[drive] = d + drive_set.add(drive) + finally: + pass + return res diff --git a/src/calibre/devices/user_defined/__init__.py b/src/calibre/devices/user_defined/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/calibre/devices/user_defined/driver.py b/src/calibre/devices/user_defined/driver.py new file mode 100644 index 0000000000..3211bd19ef --- /dev/null +++ b/src/calibre/devices/user_defined/driver.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +from calibre.devices.usbms.driver import USBMS +from calibre.ebooks import BOOK_EXTENSIONS + +class USER_DEFINED(USBMS): + + name = 'User Defined USB driver' + gui_name = 'User Defined USB Device' + author = 'Kovid Goyal' + supported_platforms = ['windows', 'osx', 'linux'] + + # Ordered list of supported formats + FORMATS = BOOK_EXTENSIONS + + VENDOR_ID = 0xFFFF + PRODUCT_ID = 0xFFFF + BCD = None + + EBOOK_DIR_MAIN = '' + EBOOK_DIR_CARD_A = '' + + VENDOR_NAME = [] + WINDOWS_MAIN_MEM = '' + WINDOWS_CARD_A_MEM = '' + + OSX_MAIN_MEM = 'Device Main Memory' + + MAIN_MEMORY_VOLUME_LABEL = 'Device Main Memory' + + SUPPORTS_SUB_DIRS = True + + EXTRA_CUSTOMIZATION_MESSAGE = [ + _('USB Vendor ID (in hex)') + ':::' + + _('Get this ID using Preferences -> Misc -> Get information to ' + 'set up the user-defined device'), + _('USB Product ID (in hex)')+ ':::' + + _('Get this ID using Preferences -> Misc -> Get information to ' + 'set up the user-defined device'), + _('USB Revision ID (in hex)')+ ':::' + + _('Get this ID using Preferences -> Misc -> Get information to ' + 'set up the user-defined device'), + _('Windows main memory vendor string') + ':::' + + _('This field is used only on windows. ' + 'Get this ID using Preferences -> Misc -> Get information to ' + 'set up the user-defined device'), + _('Windows main memory ID string') + ':::' + + _('This field is used only on windows. ' + 'Get this ID using Preferences -> Misc -> Get information to ' + 'set up the user-defined device'), + _('Windows card A vendor string') + ':::' + + _('This field is used only on windows. ' + 'Get this ID using Preferences -> Misc -> Get information to ' + 'set up the user-defined device'), + _('Windows card A ID string') + ':::' + + _('This field is used only on windows. ' + 'Get this ID using Preferences -> Misc -> Get information to ' + 'set up the user-defined device'), + _('Main memory folder') + ':::' + + _('Enter the folder where the books are to be stored. This folder ' + 'is prepended to any send_to_device template'), + _('Card A folder') + ':::' + + _('Enter the folder where the books are to be stored. This folder ' + 'is prepended to any send_to_device template'), + ] + EXTRA_CUSTOMIZATION_DEFAULT = [ + '0x0000', + '0x0000', + '0x0000', + '', + '', + '', + '', + '', + '', + ] + OPT_USB_VENDOR_ID = 0 + OPT_USB_PRODUCT_ID = 1 + OPT_USB_REVISION_ID = 2 + OPT_USB_WINDOWS_MM_VEN_ID = 3 + OPT_USB_WINDOWS_MM_ID = 4 + OPT_USB_WINDOWS_CA_VEN_ID = 5 + OPT_USB_WINDOWS_CA_ID = 6 + OPT_MAIN_MEM_FOLDER = 7 + OPT_CARD_A_FOLDER = 8 + + def initialize(self): + try: + e = self.settings().extra_customization + self.VENDOR_ID = int(e[self.OPT_USB_VENDOR_ID], 16) + self.PRODUCT_ID = int(e[self.OPT_USB_PRODUCT_ID], 16) + self.BCD = [int(e[self.OPT_USB_REVISION_ID], 16)] + if e[self.OPT_USB_WINDOWS_MM_VEN_ID]: + self.VENDOR_NAME.append(e[self.OPT_USB_WINDOWS_MM_VEN_ID]) + if e[self.OPT_USB_WINDOWS_CA_VEN_ID] and \ + e[self.OPT_USB_WINDOWS_CA_VEN_ID] not in self.VENDOR_NAME: + self.VENDOR_NAME.append(e[self.OPT_USB_WINDOWS_CA_VEN_ID]) + self.WINDOWS_MAIN_MEM = e[self.OPT_USB_WINDOWS_MM_ID] + '&' + self.WINDOWS_CARD_A_MEM = e[self.OPT_USB_WINDOWS_CA_ID] + '&' + self.EBOOK_DIR_MAIN = e[self.OPT_MAIN_MEM_FOLDER] + self.EBOOK_DIR_CARD_A = e[self.OPT_CARD_A_FOLDER] + except: + import traceback + traceback.print_exc() + USBMS.initialize(self) \ No newline at end of file diff --git a/src/calibre/gui2/preferences/device_user_defined.py b/src/calibre/gui2/preferences/device_user_defined.py new file mode 100644 index 0000000000..9b193078be --- /dev/null +++ b/src/calibre/gui2/preferences/device_user_defined.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +from PyQt4.Qt import QDialog, QVBoxLayout, QPlainTextEdit, QTimer, \ + QDialogButtonBox, QPushButton, QApplication, QIcon, QMessageBox + +from calibre.constants import iswindows + +def step_dialog(parent, title, msg, det_msg=''): + d = QMessageBox(parent) + d.setWindowTitle(title) + d.setText(msg) + d.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) + return d.exec_() & QMessageBox.Cancel + + +class UserDefinedDevice(QDialog): + + def __init__(self, parent=None): + QDialog.__init__(self, parent) + self._layout = QVBoxLayout(self) + self.setLayout(self._layout) + self.log = QPlainTextEdit(self) + self._layout.addWidget(self.log) + self.log.setPlainText(_('Getting device information')+'...') + self.copy = QPushButton(_('Copy to &clipboard')) + self.copy.setDefault(True) + self.setWindowTitle(_('User-defined device information')) + self.setWindowIcon(QIcon(I('debug.png'))) + self.copy.clicked.connect(self.copy_to_clipboard) + self.ok = QPushButton('&OK') + self.ok.setAutoDefault(False) + self.ok.clicked.connect(self.accept) + self.bbox = QDialogButtonBox(self) + self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole) + self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole) + self._layout.addWidget(self.bbox) + self.resize(750, 500) + self.bbox.setEnabled(False) + QTimer.singleShot(1000, self.device_info) + + def device_info(self): + try: + from calibre.devices import device_info + r = step_dialog(self.parent(), _('Device Detection'), + _('Ensure your device is disconnected, then press OK')) + if r: + self.close() + return + before = device_info() + r = step_dialog(self.parent(), _('Device Detection'), + _('Ensure your device is connected, then press OK')) + if r: + self.close() + return + after = device_info() + new_drives = after['drive_set'] - before['drive_set'] + new_devices = after['device_set'] - before['device_set'] + res = '' + if (not iswindows or len(new_drives)) and len(new_devices) == 1: + for d in new_devices: + res = _('USB Vendor ID (in hex)') + ': 0x' + \ + after['device_details'][d][0] + '\n' + res += _('USB Product ID (in hex)') + ': 0x' + \ + after['device_details'][d][1] + '\n' + res += _('USB Revision ID (in hex)') + ': 0x' + \ + after['device_details'][d][2] + '\n' + if iswindows: + # sort the drives by the order number + for i,d in enumerate(sorted(new_drives, + key=lambda x: after['drive_details'][x][0])): + if i == 0: + res += _('Windows main memory ID string') + ': ' + \ + after['drive_details'][d][1] + '\n' + res += _('Windows main memory ID string') + ': ' + \ + after['drive_details'][d][2] + '\n' + else: + res += _('Windows card A vendor string') + ': ' + \ + after['drive_details'][d][1] + '\n' + res += _('Windows card A ID string') + ': ' + \ + after['drive_details'][d][2] + '\n' + trailer = _( + 'Copy these values to the clipboard, paste them into an ' + 'editor, then enter them into the USER_DEVICE by ' + 'customizing the device plugin in Preferences->Plugins. ' + 'Remember to also enter the folders where you want the books to ' + 'be put. You must restart calibre for your changes ' + 'to take effect.\n') + self.log.setPlainText(res + '\n\n' + trailer) + finally: + self.bbox.setEnabled(True) + + def copy_to_clipboard(self): + QApplication.clipboard().setText(self.log.toPlainText()) + +if __name__ == '__main__': + app = QApplication([]) + d = UserDefinedDevice() + d.exec_() diff --git a/src/calibre/gui2/preferences/misc.py b/src/calibre/gui2/preferences/misc.py index ead5da4ce4..80bfdffcd8 100644 --- a/src/calibre/gui2/preferences/misc.py +++ b/src/calibre/gui2/preferences/misc.py @@ -30,6 +30,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('enforce_cpu_limit', config, restart_required=True) self.device_detection_button.clicked.connect(self.debug_device_detection) self.button_open_config_dir.clicked.connect(self.open_config_dir) + self.user_defined_device_button.clicked.connect(self.user_defined_device) self.button_osx_symlinks.clicked.connect(self.create_symlinks) self.button_osx_symlinks.setVisible(isosx) @@ -38,6 +39,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): d = DebugDevice(self) d.exec_() + def user_defined_device(self, *args): + from calibre.gui2.preferences.device_user_defined import UserDefinedDevice + d = UserDefinedDevice(self) + d.exec_() + def open_config_dir(self, *args): from calibre.utils.config import config_dir open_local_file(config_dir) diff --git a/src/calibre/gui2/preferences/misc.ui b/src/calibre/gui2/preferences/misc.ui index 8b0189b0a1..843f0f01b7 100644 --- a/src/calibre/gui2/preferences/misc.ui +++ b/src/calibre/gui2/preferences/misc.ui @@ -58,7 +58,14 @@ - + + + + Get information to setup the &user defined device + + + + Qt::Vertical