diff --git a/src/calibre/debug.py b/src/calibre/debug.py index 962681a267..45ce9987e0 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -17,22 +17,25 @@ def option_parser(): Run an embedded python interpreter. ''') - parser.add_option('--update-module', help='Update the specified module in the frozen library. '+ - 'Module specifications are of the form full.name.of.module,path_to_module.py', default=None + parser.add_option('--update-module', + help='Update the specified module in the frozen library. '+ + 'Module specifications are of the form full.name.of.module,path_to_module.py', + default=None ) parser.add_option('-c', '--command', help='Run python code.', default=None) parser.add_option('-e', '--exec-file', default=None, help='Run the python code in file.') - parser.add_option('-d', '--debug-device-driver', default=False, action='store_true', + parser.add_option('-d', '--debug-device-driver', default=False, action='store_true', help='Debug the specified device driver.') parser.add_option('-g', '--gui', default=False, action='store_true', help='Run the GUI',) - parser.add_option('--migrate', action='store_true', default=False, - help='Migrate old database. Needs two arguments. Path to library1.db and path to new library folder.') + parser.add_option('--migrate', action='store_true', default=False, + help='Migrate old database. Needs two arguments. Path ' + 'to library1.db and path to new library folder.') return parser def update_zipfile(zipfile, mod, path): if 'win32' in sys.platform: - print 'WARNING: On Windows Vista using this option may cause windows to put library.zip into the Virtual Store (typically located in c:\Users\username\AppData\Local\VirtualStore). If it does this you must delete it from there after you\'re done debugging).' + print 'WARNING: On Windows Vista using this option may cause windows to put library.zip into the Virtual Store (typically located in c:\Users\username\AppData\Local\VirtualStore). If it does this you must delete it from there after you\'re done debugging).' pat = re.compile(mod.replace('.', '/')+r'\.py[co]*') name = mod.replace('.', '/') + os.path.splitext(path)[-1] update(zipfile, [pat], [path], [name]) @@ -46,8 +49,8 @@ def update_module(mod, path): zp = os.path.join(os.path.dirname(sys.executable), 'library.zip') elif isosx: zp = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')), - 'Resources', 'lib', - 'python'+'.'.join(map(str, sys.version_info[:2])), + 'Resources', 'lib', + 'python'+'.'.join(map(str, sys.version_info[:2])), 'site-packages.zip') else: zp = os.path.join(getattr(sys, 'frozen_path'), 'loader.zip') @@ -71,23 +74,23 @@ def setRange(self, min, max): self.max = max def setValue(self, val): self.update(float(val)/getattr(self, 'max', 1)) - + db = LibraryDatabase(old) db2 = LibraryDatabase2(new) db2.migrate_old(db, Dummy(terminal_controller, 'Migrating database...')) prefs['library_path'] = os.path.abspath(new) print 'Database migrated to', os.path.abspath(new) - + def debug_device_driver(): from calibre.devices.scanner import DeviceScanner s = DeviceScanner() s.scan() print 'USB devices on system:', repr(s.devices) if iswindows: - wmi = __import__('wmi', globals(), locals(), [], -1) + wmi = __import__('wmi', globals(), locals(), [], -1) drives = [] print 'Drives detected:' - print '\t', '(ID, Partitions, Drive letter)' + print '\t', '(ID, Partitions, Drive letter)' for drive in wmi.WMI().Win32_DiskDrive(): if drive.Partitions == 0: continue @@ -111,7 +114,7 @@ def debug_device_driver(): d.open() print 'Total space:', d.total_space() break - + def main(args=sys.argv): opts, args = option_parser().parse_args(args) diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index 14b056944b..a5775dec8a 100755 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -4,32 +4,32 @@ Device driver for Amazon's Kindle ''' -import os, re +import os, re, sys from calibre.devices.usbms.driver import USBMS, metadata_from_formats class KINDLE(USBMS): # Ordered list of supported formats FORMATS = ['azw', 'mobi', 'prc', 'azw1', 'tpz', 'txt'] - + VENDOR_ID = [0x1949] PRODUCT_ID = [0x0001] BCD = [0x399] - + VENDOR_NAME = 'KINDLE' WINDOWS_MAIN_MEM = 'INTERNAL_STORAGE' WINDOWS_CARD_MEM = 'CARD_STORAGE' - + OSX_MAIN_MEM = 'Kindle Internal Storage Media' OSX_CARD_MEM = 'Kindle Card Storage Media' - + MAIN_MEMORY_VOLUME_LABEL = 'Kindle Main Memory' STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card' - + EBOOK_DIR_MAIN = "documents" EBOOK_DIR_CARD = "documents" SUPPORTS_SUB_DIRS = True - + WIRELESS_FILE_NAME_PATTERN = re.compile( r'(?P
'+ _('Could not email the following books '
+ 'as no suitable formats were found:
'+_('Failed to email the following books:')+\ + '
Cannot upload books to device there ' + 'is no more free space available ')+where+ + '
\n'+_('If checked, downloaded news will be automatically '
+ 'mailed '+_('Could not fetch metadata from:')+\
- ' '+ _('Warning')+':'+\
+ _('Could not fetch metadata from:')+\
+ ' For help visit %s.kovidgoyal.net For help visit %s.kovidgoyal.net'
+ ' %s support is still in beta. If you find bugs, please report them by opening a ticket.'%of).exec_()
+ warning_dialog(self, 'Warning',
+ (' %s support is still in beta. If you find bugs, '
+ 'please report them by opening a ticket.')%of).exec_()
prefs.set('output_format', of)
-
-
+
+
def test_server(self, *args):
if self.content_server.exception is not None:
- error_dialog(self, _('Failed to start content server'),
+ error_dialog(self, _('Failed to start content server'),
unicode(self.content_server.exception)).exec_()
def show_similar_books(self, type):
@@ -432,13 +482,14 @@ def show_similar_books(self, type):
elif type == 'author':
authors = idx.model().db.authors(row)
if authors:
- search = ['author:'+a.strip().replace('|', ',') for a in authors.split(',')]
+ search = ['author:'+a.strip().replace('|', ',') \
+ for a in authors.split(',')]
join = ' or '
if search:
self.search.set_search_string(join.join(search))
-
-
-
+
+
+
def toggle_cover_flow(self, show):
if config['separate_cover_flow']:
if show:
@@ -453,8 +504,9 @@ def toggle_cover_flow(self, show):
self.cover_flow.setFocus(Qt.OtherFocusReason)
self.library_view.scrollTo(self.library_view.currentIndex())
d.show()
- self.connect(d, SIGNAL('finished(int)'),
- lambda x: self.status_bar.cover_flow_button.setChecked(False))
+ self.connect(d, SIGNAL('finished(int)'),
+ lambda x: self.status_bar.\
+ cover_flow_button.setChecked(False))
self.cf_dialog = d
else:
cfd = getattr(self, 'cf_dialog', None)
@@ -464,7 +516,8 @@ def toggle_cover_flow(self, show):
self.cf_dialog = None
else:
if show:
- self.library_view.setCurrentIndex(self.library_view.currentIndex())
+ self.library_view.setCurrentIndex(
+ self.library_view.currentIndex())
self.cover_flow.setVisible(True)
self.cover_flow.setFocus(Qt.OtherFocusReason)
#self.status_bar.book_info.book_data.setMaximumHeight(100)
@@ -489,7 +542,8 @@ def toggle_tags_view(self, show):
self.popularity.setVisible(False)
def sync_cf_to_listview(self, index, *args):
- if not hasattr(index, 'row') and self.library_view.currentIndex().row() != index:
+ if not hasattr(index, 'row') and \
+ self.library_view.currentIndex().row() != index:
index = self.library_view.model().index(index, 0)
self.library_view.setCurrentIndex(index)
if hasattr(index, 'row') and self.cover_flow.isVisible() and \
@@ -503,7 +557,8 @@ def another_instance_wants_to_talk(self, msg):
path = os.path.abspath(argv[1])
if os.access(path, os.R_OK):
self.add_filesystem_book(path)
- self.setWindowState(self.windowState() & ~Qt.WindowMinimized|Qt.WindowActive)
+ self.setWindowState(self.windowState() & \
+ ~Qt.WindowMinimized|Qt.WindowActive)
self.show()
self.raise_()
self.activateWindow()
@@ -535,22 +590,26 @@ def device_detected(self, connected):
Called when a device is connected to the computer.
'''
if connected:
- self.device_manager.get_device_information(Dispatcher(self.info_read))
- self.set_default_thumbnail(self.device_manager.device.THUMBNAIL_HEIGHT)
+ self.device_manager.get_device_information(\
+ Dispatcher(self.info_read))
+ self.set_default_thumbnail(\
+ self.device_manager.device.THUMBNAIL_HEIGHT)
self.status_bar.showMessage(_('Device: ')+\
- self.device_manager.device.__class__.__name__+_(' detected.'), 3000)
- self.action_sync.setEnabled(True)
+ self.device_manager.device.__class__.__name__+\
+ _(' detected.'), 3000)
self.device_connected = True
+ self._sync_menu.enable_device_actions(True)
else:
self.device_connected = False
+ self._sync_menu.enable_device_actions(False)
self.location_view.model().update_devices()
- self.action_sync.setEnabled(False)
- self.vanity.setText(self.vanity_template%dict(version=self.latest_version, device=' '))
+ self.vanity.setText(self.vanity_template%\
+ dict(version=self.latest_version, device=' '))
self.device_info = ' '
if self.current_view() != self.library_view:
self.status_bar.reset_info()
self.location_selected('library')
-
+
def info_read(self, job):
'''
Called once device information has been read.
@@ -561,10 +620,11 @@ def info_read(self, job):
info, cp, fs = job.result
self.location_view.model().update_devices(cp, fs)
self.device_info = _('Connected ')+info[0]
- self.vanity.setText(self.vanity_template%dict(version=self.latest_version, device=self.device_info))
+ self.vanity.setText(self.vanity_template%\
+ dict(version=self.latest_version, device=self.device_info))
self.device_manager.books(Dispatcher(self.metadata_downloaded))
-
+
def metadata_downloaded(self, job):
'''
Called once metadata has been read for all books on the device.
@@ -595,41 +655,22 @@ def metadata_downloaded(self, job):
############################################################################
- ############################# Upload booklists #############################
- def upload_booklists(self):
- '''
- Upload metadata to device.
- '''
- self.device_manager.sync_booklists(Dispatcher(self.metadata_synced),
- self.booklists())
-
- def metadata_synced(self, job):
- '''
- Called once metadata has been uploaded.
- '''
- if job.exception is not None:
- self.device_job_exception(job)
- return
- cp, fs = job.result
- self.location_view.model().update_devices(cp, fs)
- ############################################################################
-
################################# Add books ################################
def add_recursive(self, single):
- root = choose_dir(self, 'recursive book import root dir dialog',
+ root = choose_dir(self, 'recursive book import root dir dialog',
'Select root folder')
if not root:
return
from calibre.gui2.add import AddRecursive
- self._add_recursive_thread = AddRecursive(root,
+ self._add_recursive_thread = AddRecursive(root,
self.library_view.model().db, self.get_metadata,
single, self)
self.connect(self._add_recursive_thread, SIGNAL('finished()'),
self._recursive_files_added)
self._add_recursive_thread.start()
-
+
def _recursive_files_added(self):
self._add_recursive_thread.process_duplicates()
if self._add_recursive_thread.number_of_books_added > 0:
@@ -637,7 +678,7 @@ def _recursive_files_added(self):
self.library_view.model().research()
self.library_view.model().count_changed()
self._add_recursive_thread = None
-
+
def add_recursive_single(self, checked):
'''
Add books from the local filesystem to either the library or the device
@@ -663,7 +704,8 @@ def add_filesystem_book(self, path):
to_device = self.stack.currentIndex() != 0
self._add_books(books, to_device)
if to_device:
- self.status_bar.showMessage(_('Uploading books to device.'), 2000)
+ self.status_bar.showMessage(\
+ _('Uploading books to device.'), 2000)
def add_books(self, checked):
'''
@@ -671,22 +713,22 @@ def add_books(self, checked):
'''
books = choose_files(self, 'add books dialog dir', 'Select books',
filters=[
- (_('Books'), BOOK_EXTENSIONS),
- (_('EPUB Books'), ['epub']),
- (_('LRF Books'), ['lrf']),
- (_('HTML Books'), ['htm', 'html', 'xhtm', 'xhtml']),
- (_('LIT Books'), ['lit']),
- (_('MOBI Books'), ['mobi', 'prc']),
- (_('Text books'), ['txt', 'rtf']),
- (_('PDF Books'), ['pdf']),
- (_('Comics'), ['cbz', 'cbr']),
- (_('Archives'), ['zip', 'rar']),
- ])
+ (_('Books'), BOOK_EXTENSIONS),
+ (_('EPUB Books'), ['epub']),
+ (_('LRF Books'), ['lrf']),
+ (_('HTML Books'), ['htm', 'html', 'xhtm', 'xhtml']),
+ (_('LIT Books'), ['lit']),
+ (_('MOBI Books'), ['mobi', 'prc']),
+ (_('Text books'), ['txt', 'rtf']),
+ (_('PDF Books'), ['pdf']),
+ (_('Comics'), ['cbz', 'cbr']),
+ (_('Archives'), ['zip', 'rar']),
+ ])
if not books:
return
to_device = self.stack.currentIndex() != 0
self._add_books(books, to_device)
-
+
def _add_books(self, paths, to_device, on_card=None):
if on_card is None:
@@ -694,7 +736,7 @@ def _add_books(self, paths, to_device, on_card=None):
if not paths:
return
from calibre.gui2.add import AddFiles
- self._add_files_thread = AddFiles(paths, self.default_thumbnail,
+ self._add_files_thread = AddFiles(paths, self.default_thumbnail,
self.get_metadata,
None if to_device else \
self.library_view.model().db
@@ -706,63 +748,23 @@ def _add_books(self, paths, to_device, on_card=None):
self.connect(self._add_files_thread, SIGNAL('finished()'),
self._files_added)
self._add_files_thread.start()
-
+
def _files_added(self):
t = self._add_files_thread
self._add_files_thread = None
if not t.canceled:
if t.send_to_device:
- self.upload_books(t.paths,
- list(map(sanitize_file_name, t.names)),
+ self.upload_books(t.paths,
+ list(map(sanitize_file_name, t.names)),
t.infos, on_card=t.on_card)
- self.status_bar.showMessage(_('Uploading books to device.'), 2000)
+ self.status_bar.showMessage(
+ _('Uploading books to device.'), 2000)
else:
t.process_duplicates()
if t.number_of_books_added > 0:
self.library_view.model().books_added(t.number_of_books_added)
- self.db_images.reset()
-
- def upload_books(self, files, names, metadata, on_card=False, memory=None):
- '''
- Upload books to device.
- :param files: List of either paths to files or file like objects
- '''
- titles = [i['title'] for i in metadata]
- job = self.device_manager.upload_books(Dispatcher(self.books_uploaded),
- files, names, on_card=on_card,
- metadata=metadata, titles=titles
- )
- self.upload_memory[job] = (metadata, on_card, memory, files)
-
- def books_uploaded(self, job):
- '''
- Called once books have been uploaded.
- '''
- metadata, on_card, memory, files = self.upload_memory.pop(job)
-
- if job.exception is not None:
- if isinstance(job.exception, FreeSpaceError):
- where = 'in main memory.' if 'memory' in str(job.exception) else 'on the storage card.'
- titles = '\n'.join([' Cannot upload books to device there is no more free space available ')+where+
- ' Could not save the following books to disk, because the %s format is not available for them: Could not save the following books to disk, '
+ 'because the %s format is not available for them: An invalid database already exists at %s, delete it before trying to move the existing database. An invalid database already exists at '
+ '%s, delete it before trying to move the '
+ 'existing database. Could not convert: %s It is a DRMed book. You must first remove the DRM using 3rd party tools.')%(job.description.split(':')[-1], 'http://wiki.mobileread.com/wiki/DRM')).exec_()
+ error_dialog(self, _('Conversion Error'),
+ _(' Could not convert: %s It is a '
+ 'DRMed book. You must first remove the '
+ 'DRM using 3rd party tools.')%\
+ (job.description.split(':')[-1],
+ 'http://wiki.mobileread.com/wiki/DRM')).exec_()
return
except:
pass
@@ -1404,11 +1351,12 @@ def initialize_database(self):
if iswindows:
from calibre import plugins
from PyQt4.Qt import QDir
- base = plugins['winutil'][0].special_folder_path(plugins['winutil'][0].CSIDL_PERSONAL)
+ base = plugins['winutil'][0].special_folder_path(
+ plugins['winutil'][0].CSIDL_PERSONAL)
if not base or not os.path.exists(base):
base = unicode(QDir.homePath()).replace('/', os.sep)
- dir = unicode(QFileDialog.getExistingDirectory(self,
- _('Choose a location for your ebook library.'), base))
+ dir = unicode(QFileDialog.getExistingDirectory(self,
+ _('Choose a location for your ebook library.'), base))
if not dir:
dir = os.path.expanduser('~/Library')
self.library_path = os.path.abspath(dir)
@@ -1417,9 +1365,9 @@ def initialize_database(self):
os.makedirs(self.library_path)
except:
self.library_path = os.path.expanduser('~/Library')
- error_dialog(self, _('Invalid library location'),
+ error_dialog(self, _('Invalid library location'),
_('Could not access %s. Using %s as the library.')%
- (repr(self.library_path), repr(self.library_path))
+ (repr(self.library_path), repr(self.library_path))
).exec_()
os.makedirs(self.library_path)
@@ -1431,16 +1379,19 @@ def read_settings(self):
self.restoreGeometry(geometry)
set_sidebar_directories(None)
self.tool_bar.setIconSize(config['toolbar_icon_size'])
- self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if config['show_text_in_toolbar'] else Qt.ToolButtonIconOnly)
-
-
+ self.tool_bar.setToolButtonStyle(
+ Qt.ToolButtonTextUnderIcon if \
+ config['show_text_in_toolbar'] else \
+ Qt.ToolButtonIconOnly)
+
+
def write_settings(self):
config.set('main_window_geometry', self.saveGeometry())
dynamic.set('sort_column', self.library_view.model().sorted_on)
self.library_view.write_settings()
if self.device_connected:
self.memory_view.write_settings()
-
+
def quit(self, checked, restart=False):
if not self.confirm_quit():
return
@@ -1450,7 +1401,7 @@ def quit(self, checked, restart=False):
pass
self.restart_after_quit = restart
QApplication.instance().quit()
-
+
def donate(self, *args):
BUTTON = '''
'+__appname__ + _(''' is communicating with the device! '+__appname__ + \
+ _(''' is communicating with the device! %s is already running. %s %s is already running. %s
to this email address '
+ '(provided it is in one of the listed formats).')])
+
+ def rowCount(self, *args):
+ return len(self.account_order)
+
+ def columnCount(self, *args):
+ return 3
+
+ def headerData(self, section, orientation, role):
+ if role == Qt.DisplayRole and orientation == Qt.Horizontal:
+ return self.headers[section]
+ return NONE
+
+ def data(self, index, role):
+ row, col = index.row(), index.column()
+ if row < 0 or row >= self.rowCount():
+ return NONE
+ account = self.account_order[row]
+ if role == Qt.UserRole:
+ return (account, self.accounts[account])
+ if role == Qt.ToolTipRole:
+ return self.tooltips[col]
+ if role == Qt.DisplayRole:
+ if col == 0:
+ return QVariant(account)
+ if col == 1:
+ return QVariant(self.accounts[account][0])
+ if role == Qt.FontRole and self.accounts[account][2]:
+ return self.default_font
+ if role == Qt.CheckStateRole and col == 2:
+ return QVariant(Qt.Checked if self.accounts[account][1] else Qt.Unchecked)
+ return NONE
+
+ def flags(self, index):
+ if index.column() == 2:
+ return QAbstractTableModel.flags(self, index)|Qt.ItemIsUserCheckable
+ else:
+ return QAbstractTableModel.flags(self, index)|Qt.ItemIsEditable
+
+ def setData(self, index, value, role):
+ if not index.isValid():
+ return False
+ row, col = index.row(), index.column()
+ account = self.account_order[row]
+ if col == 2:
+ self.accounts[account][1] ^= True
+ elif col == 1:
+ self.accounts[account][0] = unicode(value.toString()).upper()
+ else:
+ na = unicode(value.toString())
+ from email.utils import parseaddr
+ addr = parseaddr(na)[-1]
+ if not addr:
+ return False
+ self.accounts[na] = self.accounts.pop(account)
+ self.account_order[row] = na
+ if '@kindle.com' in addr:
+ self.accounts[na][0] = 'AZW, MOBI, TPZ, PRC, AZW1'
+
+ self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'),
+ self.index(index.row(), 0), self.index(index.row(), 2))
+ return True
+
+ def make_default(self, index):
+ if index.isValid():
+ row = index.row()
+ for x in self.accounts.values():
+ x[2] = False
+ self.accounts[self.account_order[row]][2] = True
+ self.reset()
+
+ def add(self):
+ x = _('new email address')
+ y = x
+ c = 0
+ while y in self.accounts:
+ c += 1
+ y = x + str(c)
+ self.accounts[y] = ['MOBI, EPUB', True,
+ len(self.account_order) == 0]
+ self.account_order = sorted(self.accounts.keys())
+ self.reset()
+ return self.index(self.account_order.index(y), 0)
+
+ def remove(self, index):
+ if index.isValid():
+ row = self.index.row()
+ account = self.account_order[row]
+ self.accounts.pop(account)
+ self.account_order = sorted(self.accounts.keys())
+ has_default = False
+ for account in self.account_order:
+ if self.accounts[account][2]:
+ has_default = True
+ break
+ if not has_default and self.account_order:
+ self.accounts[self.account_order[0]][2] = True
+
+ self.reset()
+
class ConfigDialog(QDialog, Ui_Dialog):
@@ -141,9 +258,9 @@ def __init__(self, window, db, server=None):
self.ICON_SIZES = {0:QSize(48, 48), 1:QSize(32,32), 2:QSize(24,24)}
self.setupUi(self)
self._category_model = CategoryModel()
-
- self.connect(self.category_view, SIGNAL('activated(QModelIndex)'), lambda i: self.stackedWidget.setCurrentIndex(i.row()))
- self.connect(self.category_view, SIGNAL('clicked(QModelIndex)'), lambda i: self.stackedWidget.setCurrentIndex(i.row()))
+
+ self.category_view.currentChanged = \
+ lambda n, p: self.stackedWidget.setCurrentIndex(n.row())
self.category_view.setModel(self._category_model)
self.db = db
self.server = server
@@ -151,7 +268,7 @@ def __init__(self, window, db, server=None):
self.location.setText(path if path else '')
self.connect(self.browse_button, SIGNAL('clicked(bool)'), self.browse)
self.connect(self.compact_button, SIGNAL('clicked(bool)'), self.compact)
-
+
dirs = config['frequently_used_directories']
rn = config['use_roman_numerals_for_series_number']
self.timeout.setValue(prefs['network_timeout'])
@@ -162,20 +279,20 @@ def __init__(self, window, db, server=None):
self.connect(self.remove_button, SIGNAL('clicked(bool)'), self.remove_dir)
if not islinux:
self.dirs_box.setVisible(False)
-
+
column_map = config['column_map']
for col in column_map + [i for i in ALL_COLUMNS if i not in column_map]:
item = QListWidgetItem(BooksModel.headers[col], self.columns)
item.setData(Qt.UserRole, QVariant(col))
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
item.setCheckState(Qt.Checked if col in column_map else Qt.Unchecked)
-
+
self.connect(self.column_up, SIGNAL('clicked()'), self.up_column)
self.connect(self.column_down, SIGNAL('clicked()'), self.down_column)
self.filename_pattern = FilenamePattern(self)
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
-
+
icons = config['toolbar_icon_size']
self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2)
self.show_toolbar_text.setChecked(config['show_text_in_toolbar'])
@@ -183,7 +300,7 @@ def __init__(self, window, db, server=None):
self.book_exts = sorted(BOOK_EXTENSIONS)
for ext in self.book_exts:
self.single_format.addItem(ext.upper(), QVariant(ext))
-
+
single_format = config['save_to_disk_single_format']
self.single_format.setCurrentIndex(self.book_exts.index(single_format))
self.cover_browse.setValue(config['cover_flow_queue_length'])
@@ -204,9 +321,9 @@ def __init__(self, window, db, server=None):
items.sort(cmp=lambda x, y: cmp(x[1], y[1]))
for item in items:
self.language.addItem(item[1], QVariant(item[0]))
-
+
self.pdf_metadata.setChecked(prefs['read_file_metadata'])
-
+
added_html = False
for ext in self.book_exts:
ext = ext.lower()
@@ -242,7 +359,6 @@ def __init__(self, window, db, server=None):
self.priority.setCurrentIndex(p)
self.priority.setVisible(iswindows)
self.priority_label.setVisible(iswindows)
- self.category_view.setCurrentIndex(self._category_model.index(0))
self._plugin_model = PluginModel()
self.plugin_view.setModel(self._plugin_model)
self.connect(self.toggle_plugin, SIGNAL('clicked()'), lambda : self.modify_plugin(op='toggle'))
@@ -251,7 +367,75 @@ def __init__(self, window, db, server=None):
self.connect(self.button_plugin_browse, SIGNAL('clicked()'), self.find_plugin)
self.connect(self.button_plugin_add, SIGNAL('clicked()'), self.add_plugin)
self.separate_cover_flow.setChecked(config['separate_cover_flow'])
-
+ self.setup_email_page()
+ self.category_view.setCurrentIndex(self.category_view.model().index(0))
+
+ def setup_email_page(self):
+ opts = smtp_prefs().parse()
+ if opts.from_:
+ self.email_from.setText(opts.from_)
+ self._email_accounts = EmailAccounts(opts.accounts)
+ self.email_view.setModel(self._email_accounts)
+ if opts.relay_host:
+ self.relay_host.setText(opts.relay_host)
+ self.relay_port.setValue(opts.relay_port)
+ if opts.relay_username:
+ self.relay_username.setText(opts.relay_username)
+ if opts.relay_password:
+ self.relay_password.setText(unhexlify(opts.relay_password))
+ (self.relay_tls if opts.encryption == 'TLS' else self.relay_ssl).setChecked(True)
+ self.connect(self.relay_use_gmail, SIGNAL('clicked(bool)'),
+ self.create_gmail_relay)
+ self.connect(self.relay_show_password, SIGNAL('stateChanged(int)'),
+ lambda state:self.relay_password.setEchoMode(self.relay_password.Password))
+ self.connect(self.email_add, SIGNAL('clicked(bool)'),
+ self.add_email_account)
+ self.connect(self.email_make_default, SIGNAL('clicked(bool)'),
+ lambda c: self._email_accounts.make_default(self.email_view.currentIndex()))
+ self.email_view.resizeColumnsToContents()
+
+ def add_email_account(self, checked):
+ index = self._email_accounts.add()
+ self.email_view.setCurrentIndex(index)
+ self.email_view.resizeColumnsToContents()
+ self.email_view.edit(index)
+
+ def create_gmail_relay(self, *args):
+ self.relay_username.setText('@gmail.com')
+ self.relay_password.setText('')
+ self.relay_host.setText('smtp.gmail.com')
+ self.relay_port.setValue(587)
+ self.relay_tls.setChecked(True)
+
+ info_dialog(self, _('Finish gmail setup'),
+ _('Dont forget to enter your gmail username and password')).exec_()
+ self.relay_username.setFocus(Qt.OtherFocusReason)
+ self.relay_username.setCursorPosition(0)
+
+ def set_email_settings(self):
+ from_ = unicode(self.email_from.text()).strip()
+ if self._email_accounts.accounts and not from_:
+ error_dialog(self, _('Bad configuration'),
+ _('You must set the From email address')).exec_()
+ return False
+ username = unicode(self.relay_username.text()).strip()
+ password = unicode(self.relay_password.text()).strip()
+ host = unicode(self.relay_host.text()).strip()
+ if host and not (username and password):
+ error_dialog(self, _('Bad configuration'),
+ _('You must set the username and password for '
+ 'the mail server.')).exec_()
+ return False
+ conf = smtp_prefs()
+ conf.set('from_', from_)
+ conf.set('accounts', self._email_accounts.accounts)
+ conf.set('relay_host', host if host else None)
+ conf.set('relay_port', self.relay_port.value())
+ conf.set('relay_username', username if username else None)
+ conf.set('relay_password', hexlify(password))
+ conf.set('encryption', 'TLS' if self.relay_tls.isChecked() else 'SSL')
+ return True
+
def add_plugin(self):
path = unicode(self.plugin_path.text())
if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'):
@@ -259,22 +443,22 @@ def add_plugin(self):
self._plugin_model.populate()
self._plugin_model.reset()
else:
- error_dialog(self, _('No valid plugin path'),
+ error_dialog(self, _('No valid plugin path'),
_('%s is not a valid plugin path')%path).exec_()
-
+
def find_plugin(self):
path = choose_files(self, 'choose plugin dialog', _('Choose plugin'),
- filters=[('Plugins', ['zip'])], all_files=False,
+ filters=[('Plugins', ['zip'])], all_files=False,
select_only_single_file=True)
if path:
self.plugin_path.setText(path[0])
-
+
def modify_plugin(self, op=''):
index = self.plugin_view.currentIndex()
if index.isValid():
plugin = self._plugin_model.index_to_plugin(index)
if not plugin.can_be_disabled:
- error_dialog(self,_('Plugin cannot be disabled'),
+ error_dialog(self,_('Plugin cannot be disabled'),
_('The plugin: %s cannot be disabled')%plugin.name).exec_()
return
if op == 'toggle':
@@ -286,7 +470,7 @@ def modify_plugin(self, op=''):
if op == 'customize':
if not plugin.is_customizable():
info_dialog(self, _('Plugin not customizable'),
- _('Plugin: %s does not need customization')%plugin.name).exec_()
+ _('Plugin: %s does not need customization')%plugin.name).exec_()
return
help = plugin.customization_help()
text, ok = QInputDialog.getText(self, _('Customize %s')%plugin.name,
@@ -299,22 +483,23 @@ def modify_plugin(self, op=''):
self._plugin_model.populate()
self._plugin_model.reset()
else:
- error_dialog(self, _('Cannot remove builtin plugin'),
- plugin.name + _(' cannot be removed. It is a builtin plugin. Try disabling it instead.')).exec_()
-
-
+ error_dialog(self, _('Cannot remove builtin plugin'),
+ plugin.name + _(' cannot be removed. It is a '
+ 'builtin plugin. Try disabling it instead.')).exec_()
+
+
def up_column(self):
idx = self.columns.currentRow()
if idx > 0:
self.columns.insertItem(idx-1, self.columns.takeItem(idx))
self.columns.setCurrentRow(idx-1)
-
+
def down_column(self):
idx = self.columns.currentRow()
if idx < self.columns.count()-1:
self.columns.insertItem(idx+1, self.columns.takeItem(idx))
self.columns.setCurrentRow(idx+1)
-
+
def view_server_logs(self):
from calibre.library.server import log_access_file, log_error_file
d = QDialog(self)
@@ -336,7 +521,7 @@ def view_server_logs(self):
except IOError:
el.setPlainText('No access log found')
d.show()
-
+
def set_server_options(self):
c = server_config()
c.set('port', self.port.value())
@@ -345,7 +530,7 @@ def set_server_options(self):
if not p:
p = None
c.set('password', p)
-
+
def start_server(self):
self.set_server_options()
from calibre.library.server import start_threaded_server
@@ -353,13 +538,13 @@ def start_server(self):
while not self.server.is_running and self.server.exception is None:
time.sleep(1)
if self.server.exception is not None:
- error_dialog(self, _('Failed to start content server'),
+ error_dialog(self, _('Failed to start content server'),
unicode(self.server.exception)).exec_()
return
self.start.setEnabled(False)
self.test.setEnabled(True)
self.stop.setEnabled(True)
-
+
def stop_server(self):
from calibre.library.server import stop_threaded_server
stop_threaded_server(self.server)
@@ -367,16 +552,17 @@ def stop_server(self):
self.start.setEnabled(True)
self.test.setEnabled(False)
self.stop.setEnabled(False)
-
+
def test_server(self):
QDesktopServices.openUrl(QUrl('http://127.0.0.1:'+str(self.port.value())))
-
+
def compact(self, toggled):
d = Vacuum(self, self.db)
d.exec_()
def browse(self):
- dir = choose_dir(self, 'database location dialog', 'Select database location')
+ dir = choose_dir(self, 'database location dialog',
+ _('Select database location'))
if dir:
self.location.setText(dir)
@@ -393,7 +579,10 @@ def remove_dir(self):
def accept(self):
mcs = unicode(self.max_cover_size.text()).strip()
if not re.match(r'\d+x\d+', mcs):
- error_dialog(self, _('Invalid size'), _('The size %s is invalid. must be of the form widthxheight')%mcs).exec_()
+ error_dialog(self, _('Invalid size'),
+ _('The size %s is invalid. must be of the form widthxheight')%mcs).exec_()
+ return
+ if not self.set_email_settings():
return
config['use_roman_numerals_for_series_number'] = bool(self.roman_numerals.isChecked())
config['new_version_notification'] = bool(self.new_version_notification.isChecked())
@@ -429,18 +618,21 @@ def accept(self):
if self.viewer.item(i).checkState() == Qt.Checked:
fmts.append(str(self.viewer.item(i).text()))
config['internally_viewed_formats'] = fmts
-
+
if not path or not os.path.exists(path) or not os.path.isdir(path):
d = error_dialog(self, _('Invalid database location'),
- _('Invalid database location ')+path+_('
Must be a directory.'))
+ _('Invalid database location ')+path+
+ _('
Must be a directory.'))
d.exec_()
elif not os.access(path, os.W_OK):
d = error_dialog(self, _('Invalid database location'),
- _('Invalid database location.
Cannot write to ')+path)
+ _('Invalid database location.
Cannot write to ')+path)
d.exec_()
else:
self.database_location = os.path.abspath(path)
- self.directories = [qstring_to_unicode(self.directory_list.item(i).text()) for i in range(self.directory_list.count())]
+ self.directories = [
+ qstring_to_unicode(self.directory_list.item(i).text()) for i in \
+ range(self.directory_list.count())]
config['frequently_used_directories'] = self.directories
QDialog.accept(self)
@@ -448,7 +640,8 @@ class Vacuum(QMessageBox):
def __init__(self, parent, db):
self.db = db
- QMessageBox.__init__(self, QMessageBox.Information, _('Compacting...'), _('Compacting database. This may take a while.'),
+ QMessageBox.__init__(self, QMessageBox.Information, _('Compacting...'),
+ _('Compacting database. This may take a while.'),
QMessageBox.NoButton, parent)
QTimer.singleShot(200, self.vacuum)
@@ -456,3 +649,11 @@ def vacuum(self):
self.db.vacuum()
self.accept()
+if __name__ == '__main__':
+ from calibre.library.database2 import LibraryDatabase2
+ from PyQt4.Qt import QApplication
+ app = QApplication([])
+ d=ConfigDialog(None, LibraryDatabase2('/tmp'))
+ d.category_view.setCurrentIndex(d.category_view.model().index(2))
+ d.show()
+ app.exec_()
diff --git a/src/calibre/gui2/dialogs/config.ui b/src/calibre/gui2/dialogs/config.ui
index 9f734f9a68..9aa3fd6971 100644
--- a/src/calibre/gui2/dialogs/config.ui
+++ b/src/calibre/gui2/dialogs/config.ui
@@ -6,7 +6,7 @@
'.join(['%s: %s'%(name, exc) for name,exc in warnings])
- warning_dialog(self, _('Warning'),
- '
'+warnings+'
'+warnings+'
")
+ return QVariant(_("Double click to edit me
"))
return NONE
def headerData(self, section, orientation, role):
@@ -1060,11 +1060,11 @@ def search_from_tokens(self, tokens, all):
if not all:
ans = '[' + ans + ']'
self.set_search_string(ans)
-
+
def search_from_tags(self, tags, all):
joiner = ' and ' if all else ' or '
self.set_search_string(joiner.join(tags))
-
+
def set_search_string(self, txt):
self.normalize_state()
self.setText(txt)
diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py
index efd4e528ba..0a4be750a2 100644
--- a/src/calibre/gui2/main.py
+++ b/src/calibre/gui2/main.py
@@ -1,6 +1,7 @@
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal
')%(__appname__, __appname__)
- self.vanity_template += _('%s: %s by Kovid Goyal %%(version)s
%%(device)s
')%(__appname__, __appname__)
+ self.vanity_template += _('%s: %s by Kovid Goyal '
+ '%%(version)s
%%(device)s%s
'%(titles,))
- d.exec_()
- else:
- self.device_job_exception(job)
- return
-
- self.device_manager.add_books_to_metadata(job.result, metadata, self.booklists())
-
- self.upload_booklists()
-
- view = self.card_view if on_card else self.memory_view
- view.model().resort(reset=False)
- view.model().research()
- for f in files:
- getattr(f, 'close', lambda : True)()
- if memory and memory[1]:
- self.library_view.model().delete_books_by_id(memory[1])
+ if hasattr(self, 'db_images'):
+ self.db_images.reset()
############################################################################
@@ -784,7 +786,8 @@ def delete_books(self, checked):
return
view.model().delete_books(rows)
else:
- view = self.memory_view if self.stack.currentIndex() == 1 else self.card_view
+ view = self.memory_view if self.stack.currentIndex() == 1 \
+ else self.card_view
paths = view.model().paths(rows)
job = self.remove_paths(paths)
self.delete_memory[job] = (paths, view.model())
@@ -792,8 +795,9 @@ def delete_books(self, checked):
self.status_bar.showMessage(_('Deleting books from device.'), 1000)
def remove_paths(self, paths):
- return self.device_manager.delete_books(Dispatcher(self.books_deleted), paths)
-
+ return self.device_manager.delete_books(\
+ Dispatcher(self.books_deleted), paths)
+
def books_deleted(self, job):
'''
Called once deletion is done on the device
@@ -801,12 +805,13 @@ def books_deleted(self, job):
for view in (self.memory_view, self.card_view):
view.model().deletion_done(job, bool(job.exception))
if job.exception is not None:
- self.device_job_exception(job)
+ self.device_job_exception(job)
return
-
+
if self.delete_memory.has_key(job):
paths, model = self.delete_memory.pop(job)
- self.device_manager.remove_books_from_metadata(paths, self.booklists())
+ self.device_manager.remove_books_from_metadata(paths,
+ self.booklists())
model.paths_deleted(paths)
self.upload_booklists()
@@ -820,14 +825,14 @@ def edit_metadata(self, checked, bulk=None):
rows = self.library_view.selectionModel().selectedRows()
previous = self.library_view.currentIndex()
if not rows or len(rows) == 0:
- d = error_dialog(self, _('Cannot edit metadata'),
+ d = error_dialog(self, _('Cannot edit metadata'),
_('No books selected'))
d.exec_()
return
if bulk or (bulk is None and len(rows) > 1):
return self.edit_bulk_metadata(checked)
-
+
def accepted(id):
self.library_view.model().refresh_ids([id])
@@ -839,149 +844,45 @@ def accepted(id):
if rows:
current = self.library_view.currentIndex()
self.library_view.model().current_changed(current, previous)
-
+
def edit_bulk_metadata(self, checked):
'''
Edit metadata of selected books in library in bulk.
'''
- rows = [r.row() for r in self.library_view.selectionModel().selectedRows()]
+ rows = [r.row() for r in \
+ self.library_view.selectionModel().selectedRows()]
if not rows or len(rows) == 0:
- d = error_dialog(self, _('Cannot edit metadata'), _('No books selected'))
+ d = error_dialog(self, _('Cannot edit metadata'),
+ _('No books selected'))
d.exec_()
return
- if MetadataBulkDialog(self, rows, self.library_view.model().db).changed:
+ if MetadataBulkDialog(self, rows,
+ self.library_view.model().db).changed:
self.library_view.model().resort(reset=False)
self.library_view.model().research()
############################################################################
- ############################# Syncing to device#############################
- def sync_to_main_memory(self, checked, delete_from_library=False):
- self.sync_to_device(False, delete_from_library)
-
- def sync_to_card(self, checked, delete_from_library=False):
- self.sync_to_device(True, delete_from_library)
-
- def cover_to_thumbnail(self, data):
- p = QPixmap()
- p.loadFromData(data)
- if not p.isNull():
- ht = self.device_manager.device_class.THUMBNAIL_HEIGHT if self.device_manager else \
- Device.THUMBNAIL_HEIGHT
- p = p.scaledToHeight(ht, Qt.SmoothTransformation)
- return (p.width(), p.height(), pixmap_to_data(p))
-
- def sync_news(self):
- if self.device_connected:
- ids = list(dynamic.get('news_to_be_synced', set([])))
- ids = [id for id in ids if self.library_view.model().db.has_id(id)]
- files = self.library_view.model().get_preferred_formats_from_ids(
- ids, self.device_manager.device_class.FORMATS)
- files = [f for f in files if f is not None]
- if not files:
- dynamic.set('news_to_be_synced', set([]))
- return
- metadata = self.library_view.model().get_metadata(ids, rows_are_ids=True)
- names = []
- for mi in metadata:
- prefix = sanitize_file_name(mi['title'])
- if not isinstance(prefix, unicode):
- prefix = prefix.decode(preferred_encoding, 'replace')
- prefix = ascii_filename(prefix)
- names.append('%s_%d%s'%(prefix, id, os.path.splitext(f.name)[1]))
- cdata = mi['cover']
- if cdata:
- mi['cover'] = self.cover_to_thumbnail(cdata)
- dynamic.set('news_to_be_synced', set([]))
- if config['upload_news_to_device'] and files:
- remove = ids if config['delete_news_from_library_on_upload'] else []
- on_card = self.location_view.model().free[0] < self.location_view.model().free[1]
- self.upload_books(files, names, metadata, on_card=on_card, memory=[[f.name for f in files], remove])
- self.status_bar.showMessage(_('Sending news to device.'), 5000)
-
- def send_specific_format_to_device(self):
- d = ChooseFormatDialog(self, _('Choose format to send to device'),
- self.device_manager.device_class.FORMATS)
- d.exec_()
- fmt = d.format().lower()
- on_card = config['send_to_storage_card_by_default']
- self.sync_to_device(on_card, False, specific_format=fmt)
-
-
- def sync_to_device(self, on_card, delete_from_library, specific_format=None):
- rows = self.library_view.selectionModel().selectedRows()
- if not self.device_manager or not rows or len(rows) == 0:
- return
- ids = iter(self.library_view.model().id(r) for r in rows)
- metadata, full_metadata = self.library_view.model().get_metadata(
- rows, full_metadata=True)
- for mi in metadata:
- cdata = mi['cover']
- if cdata:
- mi['cover'] = self.cover_to_thumbnail(cdata)
- metadata, full_metadata = iter(metadata), iter(full_metadata)
- _files = self.library_view.model().get_preferred_formats(rows,
- self.device_manager.device_class.FORMATS,
- paths=True, set_metadata=True,
- specific_format=specific_format)
- files = [getattr(f, 'name', None) for f in _files]
- bad, good, gf, names, remove_ids = [], [], [], [], []
- for f in files:
- mi, smi = metadata.next(), full_metadata.next()
- id = ids.next()
- if f is None:
- bad.append(mi['title'])
- else:
- remove_ids.append(id)
- try:
- with open(f, 'r+b') as _f:
- set_metadata(_f, smi, f.rpartition('.')[2])
- except:
- print 'Error setting metadata in book:', mi['title']
- traceback.print_exc()
- good.append(mi)
- gf.append(f)
- t = mi['title']
- if not t:
- t = _('Unknown')
- a = mi['authors']
- if not a:
- a = _('Unknown')
- prefix = sanitize_file_name(t+' - '+a)
- if not isinstance(prefix, unicode):
- prefix = prefix.decode(preferred_encoding, 'replace')
- prefix = ascii_filename(prefix)
- names.append('%s_%d%s'%(prefix, id, os.path.splitext(f)[1]))
- remove = remove_ids if delete_from_library else []
- self.upload_books(gf, names, good, on_card, memory=(_files, remove))
- self.status_bar.showMessage(_('Sending books to device.'), 5000)
- if bad:
- bad = '\n'.join('%s
')%(bad,))
- d.exec_()
-
-
- ############################################################################
############################## Save to disk ################################
def save_single_format_to_disk(self, checked):
self.save_to_disk(checked, True, config['save_to_disk_single_format'])
-
+
def save_to_single_dir(self, checked):
self.save_to_disk(checked, True)
def save_to_disk(self, checked, single_dir=False, single_format=None):
-
+
rows = self.current_view().selectionModel().selectedRows()
if not rows or len(rows) == 0:
- d = error_dialog(self, _('Cannot save to disk'), _('No books selected'))
+ d = error_dialog(self, _('Cannot save to disk'),
+ _('No books selected'))
d.exec_()
return
-
+
progress = ProgressDialog(_('Saving to disk...'), min=0, max=len(rows),
parent=self)
-
+
def callback(count, msg):
progress.set_value(count)
progress.set_msg(_('Saved')+' '+msg)
@@ -989,11 +890,12 @@ def callback(count, msg):
QApplication.sendPostedEvents()
QApplication.flush()
return not progress.canceled
-
- dir = choose_dir(self, 'save to disk dialog', _('Choose destination directory'))
+
+ dir = choose_dir(self, 'save to disk dialog',
+ _('Choose destination directory'))
if not dir:
return
-
+
progress.show()
QApplication.processEvents()
QApplication.sendPostedEvents()
@@ -1001,24 +903,29 @@ def callback(count, msg):
try:
if self.current_view() == self.library_view:
failures = self.current_view().model().save_to_disk(rows, dir,
- single_dir=single_dir, callback=callback,
+ single_dir=single_dir,
+ callback=callback,
single_format=single_format)
if failures and single_format is not None:
- msg = _('')%single_format.upper()
+ msg = _('
'''
- MSG = _('is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development.')
+ MSG = _('is the result of the efforts of many volunteers from all '
+ 'over the world. If you find it useful, please consider '
+ 'donating to support its development.')
HTML = u'''
@@ -1478,16 +1431,17 @@ def donate(self, *args):
pt.write(HTML.encode('utf-8'))
pt.close()
QDesktopServices.openUrl(QUrl.fromLocalFile(pt.name))
-
-
+
+
def confirm_quit(self):
if self.job_manager.has_jobs():
msg = _('There are active jobs. Are you sure you want to quit?')
if self.job_manager.has_device_jobs():
- msg = '')\
+ %single_format.upper()
for f in failures:
msg += '
'
- warning_dialog(self, _('Could not save some ebooks'), msg).exec_()
+ warning_dialog(self, _('Could not save some ebooks'),
+ msg).exec_()
QDesktopServices.openUrl(QUrl('file:'+dir))
else:
paths = self.current_view().model().paths(rows)
- self.device_manager.save_books(Dispatcher(self.books_saved), paths, dir)
+ self.device_manager.save_books(
+ Dispatcher(self.books_saved), paths, dir)
finally:
progress.hide()
-
+
def books_saved(self, job):
if job.exception is not None:
- self.device_job_exception(job)
+ self.device_job_exception(job)
return
############################################################################
@@ -1026,12 +933,14 @@ def books_saved(self, job):
############################### Fetch news #################################
def download_scheduled_recipe(self, recipe, script, callback):
- func, args, desc, fmt, temp_files = fetch_scheduled_recipe(recipe, script)
- job = self.job_manager.run_job(Dispatcher(self.scheduled_recipe_fetched), func, args=args,
- description=desc)
+ func, args, desc, fmt, temp_files = \
+ fetch_scheduled_recipe(recipe, script)
+ job = self.job_manager.run_job(
+ Dispatcher(self.scheduled_recipe_fetched), func, args=args,
+ description=desc)
self.conversion_jobs[job] = (temp_files, fmt, recipe, callback)
self.status_bar.showMessage(_('Fetching news from ')+recipe.title, 2000)
-
+
def scheduled_recipe_fetched(self, job):
temp_files, fmt, recipe, callback = self.conversion_jobs.pop(job)
pt = temp_files[0]
@@ -1046,15 +955,17 @@ def scheduled_recipe_fetched(self, job):
callback(recipe)
self.status_bar.showMessage(recipe.title + _(' fetched.'), 3000)
self.sync_news()
-
+
############################################################################
############################### Convert ####################################
-
+
def get_books_for_conversion(self):
- rows = [r.row() for r in self.library_view.selectionModel().selectedRows()]
+ rows = [r.row() for r in \
+ self.library_view.selectionModel().selectedRows()]
if not rows or len(rows) == 0:
- d = error_dialog(self, _('Cannot convert'), _('No books selected'))
+ d = error_dialog(self, _('Cannot convert'),
+ _('No books selected'))
d.exec_()
return [], []
comics, others = [], []
@@ -1068,50 +979,53 @@ def get_books_for_conversion(self):
else:
others.append(r)
return comics, others
-
-
+
+
def convert_bulk(self, checked):
r = self.get_books_for_conversion()
if r is None:
return
- comics, others = r
-
- res = convert_bulk_ebooks(self, self.library_view.model().db, comics, others)
+ comics, others = r
+
+ res = convert_bulk_ebooks(self,
+ self.library_view.model().db, comics, others)
if res is None:
return
- jobs, changed = res
+ jobs, changed = res
for func, args, desc, fmt, id, temp_files in jobs:
- job = self.job_manager.run_job(Dispatcher(self.book_converted),
+ job = self.job_manager.run_job(Dispatcher(self.book_converted),
func, args=args, description=desc)
self.conversion_jobs[job] = (temp_files, fmt, id)
-
+
if changed:
self.library_view.model().resort(reset=False)
self.library_view.model().research()
-
+
def set_conversion_defaults(self, checked):
set_conversion_defaults(False, self, self.library_view.model().db)
-
+
def set_comic_conversion_defaults(self, checked):
set_conversion_defaults(True, self, self.library_view.model().db)
-
+
def convert_single(self, checked):
r = self.get_books_for_conversion()
if r is None: return
previous = self.library_view.currentIndex()
- rows = [x.row() for x in self.library_view.selectionModel().selectedRows()]
+ rows = [x.row() for x in \
+ self.library_view.selectionModel().selectedRows()]
comics, others = r
- jobs, changed = convert_single_ebook(self, self.library_view.model().db, comics, others)
+ jobs, changed = convert_single_ebook(self,
+ self.library_view.model().db, comics, others)
for func, args, desc, fmt, id, temp_files in jobs:
- job = self.job_manager.run_job(Dispatcher(self.book_converted),
+ job = self.job_manager.run_job(Dispatcher(self.book_converted),
func, args=args, description=desc)
self.conversion_jobs[job] = (temp_files, fmt, id)
-
+
if changed:
self.library_view.model().refresh_rows(rows)
current = self.library_view.currentIndex()
self.library_view.model().current_changed(current, previous)
-
+
def book_converted(self, job):
temp_files, fmt, book_id = self.conversion_jobs.pop(job)
try:
@@ -1119,9 +1033,11 @@ def book_converted(self, job):
self.job_exception(job)
return
data = open(temp_files[-1].name, 'rb')
- self.library_view.model().db.add_format(book_id, fmt, data, index_is_id=True)
+ self.library_view.model().db.add_format(book_id, \
+ fmt, data, index_is_id=True)
data.close()
- self.status_bar.showMessage(job.description + (' completed'), 2000)
+ self.status_bar.showMessage(job.description + \
+ (' completed'), 2000)
finally:
for f in temp_files:
try:
@@ -1133,18 +1049,19 @@ def book_converted(self, job):
if self.current_view() is self.library_view:
current = self.library_view.currentIndex()
self.library_view.model().current_changed(current, QModelIndex())
-
+
#############################View book######################################
def view_format(self, row, format):
- self._view_file(self.library_view.model().db.format(row, format, as_file=True).name)
-
+ self._view_file(self.library_view.model().db.format(row,
+ format, as_file=True).name)
+
def book_downloaded_for_viewing(self, job):
if job.exception:
- self.device_job_exception(job)
+ self.device_job_exception(job)
return
self._view_file(job.result)
-
+
def _view_file(self, name):
self.setCursor(Qt.BusyCursor)
try:
@@ -1152,13 +1069,13 @@ def _view_file(self, name):
if ext in config['internally_viewed_formats']:
if ext == 'LRF':
args = ['lrfviewer', name]
- self.job_manager.server.run_free_job('lrfviewer',
+ self.job_manager.server.run_free_job('lrfviewer',
kwdargs=dict(args=args))
else:
args = ['ebook-viewer', name]
if isosx:
args.append('--raise-window')
- self.job_manager.server.run_free_job('ebook-viewer',
+ self.job_manager.server.run_free_job('ebook-viewer',
kwdargs=dict(args=args))
else:
QDesktopServices.openUrl(QUrl('file:'+name))#launch(name)
@@ -1182,19 +1099,20 @@ def view_specific_format(self, triggered):
self.view_format(row, format)
else:
return
-
+
def view_folder(self, *args):
rows = self.current_view().selectionModel().selectedRows()
if self.current_view() is self.library_view:
if not rows or len(rows) == 0:
- d = error_dialog(self, _('Cannot open folder'), _('No book selected'))
+ d = error_dialog(self, _('Cannot open folder'),
+ _('No book selected'))
d.exec_()
return
for row in rows:
path = self.library_view.model().db.abspath(row.row())
QDesktopServices.openUrl(QUrl('file:'+path))
-
-
+
+
def view_book(self, triggered):
rows = self.current_view().selectionModel().selectedRows()
if self.current_view() is self.library_view:
@@ -1204,7 +1122,8 @@ def view_book(self, triggered):
return
row = rows[0].row()
- formats = self.library_view.model().db.formats(row).upper().split(',')
+ formats = self.library_view.model().db.formats(row).upper()
+ formats = formats.split(',')
title = self.library_view.model().db.title(row)
id = self.library_view.model().db.id(row)
format = None
@@ -1222,7 +1141,8 @@ def view_book(self, triggered):
d.exec_()
return
if format is None:
- d = ChooseFormatDialog(self, _('Choose the format to view'), formats)
+ d = ChooseFormatDialog(self, _('Choose the format to view'),
+ formats)
d.exec_()
if d.result() == QDialog.Accepted:
format = d.format()
@@ -1233,14 +1153,16 @@ def view_book(self, triggered):
else:
paths = self.current_view().model().paths(rows)
if paths:
- pt = PersistentTemporaryFile('_viewer_'+os.path.splitext(paths[0])[1])
+ pt = PersistentTemporaryFile('_viewer_'+\
+ os.path.splitext(paths[0])[1])
self.persistent_files.append(pt)
pt.close()
- self.device_manager.view_book(Dispatcher(self.book_downloaded_for_viewing),
+ self.device_manager.view_book(\
+ Dispatcher(self.book_downloaded_for_viewing),
paths[0], pt.name)
-
-
-
+
+
+
############################################################################
########################### Do advanced search #############################
@@ -1256,16 +1178,23 @@ def do_advanced_search(self, *args):
def do_config(self, *args):
if self.job_manager.has_jobs():
- d = error_dialog(self, _('Cannot configure'), _('Cannot configure while there are running jobs.'))
+ d = error_dialog(self, _('Cannot configure'),
+ _('Cannot configure while there are running jobs.'))
d.exec_()
return
- d = ConfigDialog(self, self.library_view.model().db, server=self.content_server)
+ d = ConfigDialog(self, self.library_view.model().db,
+ server=self.content_server)
d.exec_()
self.content_server = d.server
if d.result() == d.Accepted:
self.tool_bar.setIconSize(config['toolbar_icon_size'])
- self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if config['show_text_in_toolbar'] else Qt.ToolButtonIconOnly)
- self.save_menu.actions()[2].setText(_('Save only %s format to disk')%config.get('save_to_disk_single_format').upper())
+ self.tool_bar.setToolButtonStyle(
+ Qt.ToolButtonTextUnderIcon if \
+ config['show_text_in_toolbar'] else \
+ Qt.ToolButtonIconOnly)
+ self.save_menu.actions()[2].setText(
+ _('Save only %s format to disk')%config.get(
+ 'save_to_disk_single_format').upper())
if self.library_path != d.database_location:
try:
newloc = d.database_location
@@ -1276,10 +1205,12 @@ def do_config(self, *args):
pd.setCancelButton(None)
pd.setWindowTitle(_('Copying database'))
pd.show()
- self.status_bar.showMessage(_('Copying library to ')+newloc)
+ self.status_bar.showMessage(
+ _('Copying library to ')+newloc)
self.setCursor(Qt.BusyCursor)
self.library_view.setEnabled(False)
- self.library_view.model().db.move_library_to(newloc, pd)
+ self.library_view.model().db.move_library_to(
+ newloc, pd)
else:
try:
db = LibraryDatabase2(newloc)
@@ -1287,13 +1218,18 @@ def do_config(self, *args):
except Exception, err:
traceback.print_exc()
d = error_dialog(self, _('Invalid database'),
- _('
Error: %s')%(newloc, str(err)))
+ _('
Error: %s')%(newloc,
+ str(err)))
d.exec_()
- self.library_path = self.library_view.model().db.library_path
+ self.library_path = \
+ self.library_view.model().db.library_path
prefs['library_path'] = self.library_path
except Exception, err:
traceback.print_exc()
- d = error_dialog(self, _('Could not move database'), unicode(err))
+ d = error_dialog(self, _('Could not move database'),
+ unicode(err))
d.exec_()
finally:
self.unsetCursor()
@@ -1306,6 +1242,8 @@ def do_config(self, *args):
if hasattr(d, 'directories'):
set_sidebar_directories(d.directories)
self.library_view.model().read_config()
+ self.create_device_menu()
+
############################################################################
@@ -1314,7 +1252,8 @@ def do_config(self, *args):
def show_book_info(self, *args):
if self.current_view() is not self.library_view:
error_dialog(self, _('No detailed info available'),
- _('No detailed information is available for books on the device.')).exec_()
+ _('No detailed information is available for books'
+ 'on the device.')).exec_()
return
index = self.library_view.currentIndex()
if index.isValid():
@@ -1329,7 +1268,8 @@ def location_selected(self, location):
'''
page = 0 if location == 'library' else 1 if location == 'main' else 2
self.stack.setCurrentIndex(page)
- view = self.memory_view if page == 1 else self.card_view if page == 2 else None
+ view = self.memory_view if page == 1 else \
+ self.card_view if page == 2 else None
if view:
if view.resize_on_select:
view.resizeRowsToContents()
@@ -1339,27 +1279,30 @@ def location_selected(self, location):
self.status_bar.reset_info()
self.current_view().clearSelection()
if location == 'library':
- if self.device_connected:
- self.action_sync.setEnabled(True)
self.action_edit.setEnabled(True)
self.action_convert.setEnabled(True)
self.view_menu.actions()[1].setEnabled(True)
self.action_open_containing_folder.setEnabled(True)
+ self.action_sync.setEnabled(True)
else:
- self.action_sync.setEnabled(False)
self.action_edit.setEnabled(False)
self.action_convert.setEnabled(False)
self.view_menu.actions()[1].setEnabled(False)
self.action_open_containing_folder.setEnabled(False)
-
+ self.action_sync.setEnabled(False)
+
+
def device_job_exception(self, job):
'''
Handle exceptions in threaded device jobs.
'''
try:
- if 'Could not read 32 bytes on the control bus.' in unicode(job.exception):
- error_dialog(self, _('Error talking to device'),
- _('There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot.')).show()
+ if 'Could not read 32 bytes on the control bus.' in \
+ unicode(job.exception):
+ error_dialog(self, _('Error talking to device'),
+ _('There was a temporary error talking to the '
+ 'device. Please unplug and reconnect the device '
+ 'and or reboot.')).show()
return
except:
pass
@@ -1370,12 +1313,16 @@ def device_job_exception(self, job):
if not self.device_error_dialog.isVisible():
self.device_error_dialog.set_message(job.gui_text())
self.device_error_dialog.show()
-
+
def job_exception(self, job):
try:
if job.exception[0] == 'DRMError':
- error_dialog(self, _('Conversion Error'),
- _('
+ msg = '
'Quitting may cause corruption on the device.
'Are you sure you want to quit?''')+'
Traceback:
%sLog:
'%(unicode(err), unicode(tb), log))
+ d = QErrorMessage(('Error:%s
Traceback:
'
+ '%sLog:
')%(unicode(err), unicode(tb), log))
d.exec_()
diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui
index c4bea8d886..fbae01d3e6 100644
--- a/src/calibre/gui2/main.ui
+++ b/src/calibre/gui2/main.ui
@@ -1,9 +1,8 @@
-
-%s