diff --git a/Makefile b/Makefile index c9f7ca1db4..1abffcec66 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ PYTHON = python -all : plugins pictureflow gui2 translations resources +all : plugins gui2 translations resources -plugins: +plugins : src/calibre/plugins pictureflow + +src/calibre/plugins: mkdir -p src/calibre/plugins clean : diff --git a/linux_installer.py b/linux_installer.py new file mode 100644 index 0000000000..315b2d8486 --- /dev/null +++ b/linux_installer.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' +__docformat__ = 'restructuredtext en' + +''' +Create linux binary. +''' +import glob, sys, subprocess, tarfile, os, re +HOME = '/home/kovid' +PYINSTALLER = os.path.expanduser('~/build/pyinstaller') +CALIBREPREFIX = '___' +CLIT = '/usr/bin/clit' +PDFTOHTML = '/usr/bin/pdftohtml' +LIBUNRAR = '/usr/lib/libunrar.so' +QTDIR = '/usr/lib/qt4' +QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml') +EXTRAS = ('/usr/lib/python2.5/site-packages/PIL', os.path.expanduser('~/ipython/IPython')) + + +CALIBRESRC = os.path.join(CALIBREPREFIX, 'src') +CALIBREPLUGINS = os.path.join(CALIBRESRC, 'calibre', 'plugins') + +sys.path.insert(0, CALIBRESRC) +from calibre import __version__ + +def run_pyinstaller(args=sys.argv): + subprocess.check_call(('/usr/bin/sudo', 'chown', '-R', 'kovid:users', glob.glob('/usr/lib/python*/site-packages/')[-1])) + subprocess.check_call('rm -rf %(py)s/dist/* %(py)s/build/*'%dict(py=PYINSTALLER), shell=True) + subprocess.check_call('make plugins', shell=True) + cp = HOME+'/build/'+os.path.basename(os.getcwd()) + spec = open(os.path.join(PYINSTALLER, 'calibre', 'calibre.spec'), 'wb') + raw = re.sub(r'CALIBREPREFIX\s+=\s+\'___\'', 'CALIBREPREFIX = '+repr(cp), + open(__file__).read()) + spec.write(raw) + spec.close() + os.chdir(PYINSTALLER) + subprocess.check_call('python -OO Build.py calibre/calibre.spec', shell=True) + + return 0 + + +if __name__ == '__main__' and 'linux_installer.py' in __file__: + sys.exit(run_pyinstaller()) + + +loader = os.path.join(os.path.expanduser('~/temp'), 'calibre_installer_loader.py') +if not os.path.exists(loader): + open(loader, 'wb').write(''' +import sys, os +sys.frozen_path = os.getcwd() +os.chdir(os.environ.get("ORIGWD", ".")) +sys.path.insert(0, os.path.join(sys.frozen_path, "library.pyz")) +sys.path.insert(0, sys.frozen_path) +from PyQt4.QtCore import QCoreApplication +QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "plugins")]) +''') +excludes = ['gtk._gtk', 'gtk.glade', 'qt', 'matplotlib.nxutils', 'matplotlib._cntr', + 'matplotlib.ttconv', 'matplotlib._image', 'matplotlib.ft2font', + 'matplotlib._transforms', 'matplotlib._agg', 'matplotlib.backends._backend_agg', + 'matplotlib.axes', 'matplotlib', 'matplotlib.pyparsing', + 'TKinter', 'atk', 'gobject._gobject', 'pango', 'PIL', 'Image', 'IPython'] +temp = ['keyword', 'codeop'] + +recipes = ['calibre', 'web', 'feeds', 'recipes'] +prefix = '.'.join(recipes)+'.' +for f in glob.glob(os.path.join(CALIBRESRC, *(recipes+['*.py']))): + temp.append(prefix + os.path.basename(f).partition('.')[0]) +hook = os.path.expanduser('~/temp/hook-calibre.py') +f = open(hook, 'wb') +hook_script = 'hiddenimports = %s'%repr(temp) +f.write(hook_script) + +sys.path.insert(0, CALIBRESRC) +from calibre.linux import entry_points + +executables, scripts = ['calibre_postinstall', 'parallel'], \ + [os.path.join(CALIBRESRC, 'calibre', 'linux.py'), os.path.join(CALIBRESRC, 'calibre', 'parallel.py')] + +for entry in entry_points['console_scripts'] + entry_points['gui_scripts']: + fields = entry.split('=') + executables.append(fields[0].strip()) + scripts.append(os.path.join(CALIBRESRC, *map(lambda x: x.strip(), fields[1].split(':')[0].split('.')))+'.py') + +recipes = Analysis(glob.glob(os.path.join(CALIBRESRC, 'calibre', 'web', 'feeds', 'recipes', '*.py')), + pathex=[CALIBRESRC], hookspath=[os.path.dirname(hook)], excludes=excludes) +analyses = [Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), loader, script], + pathex=[PYINSTALLER, CALIBRESRC, CALIBREPLUGINS], excludes=excludes) for script in scripts] + +pyz = TOC() +binaries = TOC() + +for a in analyses: + pyz = a.pure + pyz + binaries = a.binaries + binaries +pyz = PYZ(pyz + recipes.pure, name='library.pyz') + +built_executables = [] +for script, exe, a in zip(scripts, executables, analyses): + built_executables.append(EXE(PYZ(TOC()), + a.scripts+[('O','','OPTION'),], + exclude_binaries=1, + name=os.path.join('buildcalibre', exe), + debug=False, + strip=True, + upx=False, + excludes=excludes, + console=1)) + +print 'Adding plugins...' +for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')): + binaries += [(os.path.basename(f), f, 'BINARY')] + +print 'Adding external programs...' +binaries += [('clit', CLIT, 'BINARY'), ('pdftohtml', PDFTOHTML, 'BINARY'), + ('libunrar.so', LIBUNRAR, 'BINARY')] +qt = [] +for dll in QTDLLS: + path = os.path.join(QTDIR, 'lib'+dll+'.so.4') + qt.append((os.path.basename(path), path, 'BINARY')) +binaries += qt + +plugins = [] +plugdir = os.path.join(QTDIR, 'plugins') +for dirpath, dirnames, filenames in os.walk(plugdir): + for f in filenames: + if not f.endswith('.so') or 'designer' in dirpath or 'codcs' in dirpath or 'sqldrivers' in dirpath : continue + f = os.path.join(dirpath, f) + plugins.append(('plugins/'+f.replace(plugdir, ''), f, 'BINARY')) +binaries += plugins + +manifest = '/tmp/manifest' +open(manifest, 'wb').write('\\n'.join(executables)) +version = '/tmp/version' +open(version, 'wb').write(__version__) +coll = COLLECT(binaries, pyz, + [('manifest', manifest, 'DATA'), ('version', version, 'DATA')], + *built_executables, + **dict(strip=True, + upx=False, + excludes=excludes, + name='dist')) + +os.chdir(os.path.join(HOMEPATH, 'calibre', 'dist')) +for folder in EXTRAS: + subprocess.check_call('cp -rf %s .'%folder, shell=True) + +print 'Building tarball...' +tbz2 = 'calibre-%s-i686.tar.bz2'%__version__ +tf = tarfile.open(os.path.join('/tmp', tbz2), 'w:bz2') + +for f in os.listdir('.'): + tf.add(f) diff --git a/osx_installer.py b/osx_installer.py index ce9ee98f5c..cccb46ad93 100644 --- a/osx_installer.py +++ b/osx_installer.py @@ -99,8 +99,7 @@ _check_symlinks_prescript() includes=list(self.includes) + main_modules['console'], packages=self.packages, excludes=self.excludes, - debug=debug, - ) + debug=debug) @classmethod def makedmg(cls, d, volname, @@ -249,6 +248,11 @@ _check_symlinks_prescript() else: os.link(src, os.path.join(module_dir, dest)) print + print 'Adding IPython' + dst = os.path.join(resource_dir, 'lib', 'python2.5', 'IPython') + if os.path.exists(dst): shutil.rmtree(dst) + shutil.copytree(os.path.expanduser('~/build/ipython/IPython'), dst) + print print 'Installing prescipt' sf = [os.path.basename(s) for s in all_names] cs = BuildAPP.CHECK_SYMLINKS_PRESCRIPT % dict(dest_path=repr('/usr/bin'), @@ -277,13 +281,7 @@ sys.frameworks_dir = os.path.join(os.path.dirname(os.environ['RESOURCEPATH']), ' def main(): -# auto = '--auto' in sys.argv -# if auto: -# sys.argv.remove('--auto') -# if auto and not os.path.exists('dist/auto'): -# print '%s does not exist'%os.path.abspath('dist/auto') -# return 1 -# + sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) sys.argv[1:2] = ['py2app'] setup( name = APPNAME, @@ -300,12 +298,12 @@ def main(): 'PyQt4.QtSvg', 'mechanize', 'ClientForm', 'usbobserver', 'genshi', 'calibre.web.feeds.recipes.*', - 'IPython.Extensions.*', 'pydoc'], + 'keyword', 'codeop', 'pydoc'], 'packages' : ['PIL', 'Authorization', 'rtf2xml', 'lxml'], - 'excludes' : [], + 'excludes' : ['IPython'], 'plist' : { 'CFBundleGetInfoString' : '''calibre, an E-book management application.''' ''' Visit http://calibre.kovidgoyal.net for details.''', - 'CFBundleIdentifier':'net.kovidgoyal.librs500', + 'CFBundleIdentifier':'net.kovidgoyal.calibre', 'CFBundleShortVersionString':VERSION, 'CFBundleVersion':APPNAME + ' ' + VERSION, 'LSMinimumSystemVersion':'10.4.3', @@ -316,10 +314,6 @@ def main(): }, setup_requires = ['py2app'], ) - subprocess.check_call('scp dist/*.dmg giskard:work/calibre/dist', shell=True) -# if '--shutdown' in sys.argv: -# print 'Shutting down' -# subprocess.call(('/usr/bin/sudo', '/sbin/shutdown', '-h', '+0')) return 0 if __name__ == '__main__': diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 4574ed83f8..12eeb2f625 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -1,7 +1,7 @@ ''' E-book management software''' __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -__version__ = '0.4.70' +__version__ = '0.4.72' __docformat__ = "epytext" __author__ = "Kovid Goyal " __appname__ = 'calibre' @@ -15,7 +15,7 @@ from optparse import OptionParser as _OptionParser from optparse import IndentedHelpFormatter from logging import Formatter -from PyQt4.QtCore import QSettings, QVariant, QUrl +from PyQt4.QtCore import QSettings, QVariant, QUrl, QByteArray, QString from PyQt4.QtGui import QDesktopServices from calibre.translations.msgfmt import make @@ -429,14 +429,10 @@ def singleinstance(name): class Settings(QSettings): - def __init__(self): + def __init__(self, name='calibre2'): QSettings.__init__(self, QSettings.IniFormat, QSettings.UserScope, - 'kovidgoyal.net', 'calibre') + 'kovidgoyal.net', name) - def migrate(self, settings): - for key in settings.allKeys(): - self.setValue(key, settings.value(key, QVariant())) - def get(self, key, default=None): key = str(key) if not self.contains(key): @@ -448,14 +444,27 @@ class Settings(QSettings): def set(self, key, val): val = cPickle.dumps(val, -1) - self.setValue(str(key), QVariant(val)) + self.setValue(str(key), QVariant(QByteArray(val))) _settings = Settings() -if not _settings.get('migrated from QSettings'): - _settings.migrate(QSettings('KovidsBrain', 'libprs500')) - _settings.set('migrated from QSettings', True) - _settings.sync() + +if not _settings.get('rationalized'): + __settings = Settings(name='calibre') + dbpath = os.path.join(os.path.expanduser('~'), 'library1.db').decode(sys.getfilesystemencoding()) + dbpath = unicode(__settings.value('database path', + QVariant(QString.fromUtf8(dbpath.encode('utf-8')))).toString()) + cmdline = __settings.value('LRF conversion defaults', QVariant(QByteArray(''))).toByteArray().data() + if cmdline: + cmdline = cPickle.loads(cmdline) + _settings.set('LRF conversion defaults', cmdline) + _settings.set('rationalized', True) + try: + os.unlink(unicode(__settings.fileName())) + except: + pass + _settings.set('database path', dbpath) + _spat = re.compile(r'^the\s+|^a\s+|^an\s+', re.IGNORECASE) def english_sort(x, y): ''' diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 1a3c346210..0050e091d3 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -275,10 +275,10 @@ class PRS505(Device): if not iswindows: if self._main_prefix is not None: stats = os.statvfs(self._main_prefix) - msz = stats.f_bsize * stats.f_bavail + msz = stats.f_frsize * stats.f_bavail if self._card_prefix is not None: stats = os.statvfs(self._card_prefix) - csz = stats.f_bsize * stats.f_bavail + csz = stats.f_frsize * stats.f_bavail else: msz = self._windows_space(self._main_prefix)[1] csz = self._windows_space(self._card_prefix)[1] diff --git a/src/calibre/ebooks/chardet/__init__.py b/src/calibre/ebooks/chardet/__init__.py index c0a9b45d0f..36d3b909de 100644 --- a/src/calibre/ebooks/chardet/__init__.py +++ b/src/calibre/ebooks/chardet/__init__.py @@ -46,7 +46,10 @@ def xml_to_unicode(raw, verbose=False): if match is not None: encoding = match.group(1) if encoding is None: - chardet = detect(raw) + try: + chardet = detect(raw) + except: + chardet = {'encoding':'utf-8', 'confidence':0} encoding = chardet['encoding'] if chardet['confidence'] < 1 and verbose: print 'WARNING: Encoding detection confidence %d%%'%(chardet['confidence']*100) diff --git a/src/calibre/ebooks/lrf/pylrs/pylrs.py b/src/calibre/ebooks/lrf/pylrs/pylrs.py index c089b4e378..a8cbb6fb1c 100644 --- a/src/calibre/ebooks/lrf/pylrs/pylrs.py +++ b/src/calibre/ebooks/lrf/pylrs/pylrs.py @@ -1594,6 +1594,8 @@ class Paragraph(LrsContainer): LrsContainer.__init__(self, [Text, CR, DropCaps, CharButton, LrsSimpleChar1, basestring]) if text is not None: + if isinstance(text, basestring): + text = Text(text) self.append(text) def CR(self): @@ -1914,6 +1916,8 @@ class Span(LrsSimpleChar1, LrsContainer): def __init__(self, text=None, **attrs): LrsContainer.__init__(self, [LrsSimpleChar1, Text, basestring]) if text is not None: + if isinstance(text, basestring): + text = Text(text) self.append(text) for attrname in attrs.keys(): diff --git a/src/calibre/ebooks/metadata/opf.py b/src/calibre/ebooks/metadata/opf.py index 021b63ac08..7c4d5cd1f8 100644 --- a/src/calibre/ebooks/metadata/opf.py +++ b/src/calibre/ebooks/metadata/opf.py @@ -199,7 +199,7 @@ class OPF(MetaInformation): def get_title(self): title = self.soup.package.metadata.find('dc:title') - if title: + if title and title.string: return self.ENTITY_PATTERN.sub(entity_to_unicode, title.string).strip() return self.default_title.strip() diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 2c96846aae..ee99047429 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -177,7 +177,14 @@ class MobiReader(object): opf.render(open(os.path.splitext(htmlfile)[0]+'.opf', 'wb')) def cleanup(self): - self.processed_html = re.sub(r'
', '', self.processed_html) + self.processed_html = re.sub(r'
', '', + self.processed_html) + self.processed_html = re.sub(r'<([^>]*) height="([^"]*)"', + r'<\1 style="margin-top: \2"', + self.processed_html) + self.processed_html = re.sub(r'<([^>]*) width="([^"]*)"', + r'<\1 style="text-indent: \2"', + self.processed_html) def create_opf(self, htmlfile): mi = self.book_header.exth.mi diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 78015dcd91..91ae9f0d57 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -83,17 +83,12 @@ class TableView(QTableView): def read_settings(self): - self.cw = str(Settings().value(self.__class__.__name__ + ' column widths', QVariant('')).toString()) - try: - self.cw = tuple(int(i) for i in self.cw.split(',')) - except ValueError: - self.cw = None + self.cw = Settings().get(self.__class__.__name__ + ' column widths') def write_settings(self): settings = Settings() - settings.setValue(self.__class__.__name__ + ' column widths', - QVariant(','.join(str(self.columnWidth(i)) - for i in range(self.model().columnCount(None))))) + settings.set(self.__class__.__name__ + ' column widths', + tuple([int(self.columnWidth(i)) for i in range(self.model().columnCount(None))])) def restore_column_widths(self): if self.cw and len(self.cw): @@ -107,14 +102,10 @@ class TableView(QTableView): is hidden, if True it is shown. ''' if cols: - Settings().setValue(self.__class__.__name__ + ' visible columns', - QVariant(repr(cols))) + Settings().set(self.__class__.__name__ + ' visible columns', cols) else: - cols = qstring_to_unicode(Settings().value(self.__class__.__name__ + ' visible columns', - QVariant('')).toString()) - if cols: - cols = eval(cols) - else: + cols = Settings().get(self.__class__.__name__ + ' visible columns') + if not cols: cols = [True for i in range(self.model().columnCount(self))] for i in range(len(cols)): @@ -217,7 +208,7 @@ _sidebar_directories = [] def set_sidebar_directories(dirs): global _sidebar_directories if dirs is None: - dirs = Settings().value('frequently used directories', QVariant([])).toStringList() + dirs = Settings().get('frequently used directories', []) _sidebar_directories = [QUrl.fromLocalFile(i) for i in dirs] class FileDialog(QObject): @@ -251,7 +242,7 @@ class FileDialog(QObject): self.fd.setModal(modal) self.fd.setFilter(ftext) self.fd.setWindowTitle(title) - state = settings.value(name, QVariant()).toByteArray() + state = settings.get(self.dialog_name, QByteArray()) if not self.fd.restoreState(state): self.fd.setDirectory(os.path.expanduser('~')) osu = [i for i in self.fd.sidebarUrls()] @@ -259,7 +250,7 @@ class FileDialog(QObject): QObject.connect(self.fd, SIGNAL('accepted()'), self.save_dir) self.accepted = self.fd.exec_() == QFileDialog.Accepted else: - dir = settings.value(self.dialog_name, QVariant(os.path.expanduser('~'))).toString() + dir = settings.get(self.dialog_name, os.path.expanduser('~')) self.selected_files = [] if mode == QFileDialog.AnyFile: f = qstring_to_unicode( @@ -284,7 +275,7 @@ class FileDialog(QObject): self.selected_files.append(f) if self.selected_files: self.selected_files = [qstring_to_unicode(q) for q in self.selected_files] - settings.setValue(self.dialog_name, QVariant(os.path.dirname(self.selected_files[0]))) + settings.set(self.dialog_name, os.path.dirname(self.selected_files[0])) self.accepted = bool(self.selected_files) @@ -299,7 +290,7 @@ class FileDialog(QObject): def save_dir(self): if self.fd: settings = Settings() - settings.setValue(self.dialog_name, QVariant(self.fd.saveState())) + settings.set(self.dialog_name, self.fd.saveState()) def choose_dir(window, name, title): diff --git a/src/calibre/gui2/dialogs/config.py b/src/calibre/gui2/dialogs/config.py index f94706e7cc..8c1c7fe23d 100644 --- a/src/calibre/gui2/dialogs/config.py +++ b/src/calibre/gui2/dialogs/config.py @@ -3,7 +3,7 @@ __copyright__ = '2008, Kovid Goyal ' import os from PyQt4.QtGui import QDialog, QMessageBox, QListWidgetItem, QIcon -from PyQt4.QtCore import QVariant, SIGNAL, QStringList, QTimer, Qt, QSize +from PyQt4.QtCore import SIGNAL, QTimer, Qt, QSize from calibre import islinux, Settings from calibre.gui2.dialogs.config_ui import Ui_Dialog @@ -24,18 +24,15 @@ class ConfigDialog(QDialog, Ui_Dialog): self.db = db self.current_cols = columns settings = Settings() - path = qstring_to_unicode(\ - settings.value("database path", - QVariant(os.path.join(os.path.expanduser('~'),'library1.db'))).toString()) + path = settings.get('database path') self.location.setText(os.path.dirname(path)) self.connect(self.browse_button, SIGNAL('clicked(bool)'), self.browse) self.connect(self.compact_button, SIGNAL('clicked(bool)'), self.compact) - dirs = settings.value('frequently used directories', QVariant(QStringList())).toStringList() - rn = bool(settings.value('use roman numerals for series number', - QVariant(True)).toBool()) - self.timeout.setValue(settings.value('network timeout', QVariant(5)).toInt()[0]) + dirs = settings.get('frequently used directories', []) + rn = settings.get('use roman numerals for series number', True) + self.timeout.setValue(settings.get('network timeout', 5)) self.roman_numerals.setChecked(rn) self.directory_list.addItems(dirs) self.connect(self.add_button, SIGNAL('clicked(bool)'), self.add_dir) @@ -58,7 +55,7 @@ class ConfigDialog(QDialog, Ui_Dialog): self.filename_pattern = FilenamePattern(self) self.metadata_box.layout().insertWidget(0, self.filename_pattern) - icons = settings.value('toolbar icon size', QVariant(self.ICON_SIZES[0])).toSize() + icons = settings.get('toolbar icon size', self.ICON_SIZES[0]) 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(settings.get('show text in toolbar', True)) @@ -84,14 +81,14 @@ class ConfigDialog(QDialog, Ui_Dialog): def accept(self): settings = Settings() - settings.setValue('use roman numerals for series number', QVariant(self.roman_numerals.isChecked())) - settings.setValue('network timeout', QVariant(self.timeout.value())) + settings.set('use roman numerals for series number', bool(self.roman_numerals.isChecked())) + settings.set('network timeout', int(self.timeout.value())) path = qstring_to_unicode(self.location.text()) self.final_columns = [self.columns.item(i).checkState() == Qt.Checked for i in range(self.columns.count())] - settings.setValue('toolbar icon size', QVariant(self.ICON_SIZES[self.toolbar_button_size.currentIndex()])) + settings.set('toolbar icon size', self.ICON_SIZES[self.toolbar_button_size.currentIndex()]) settings.set('show text in toolbar', bool(self.show_toolbar_text.isChecked())) pattern = self.filename_pattern.commit() - settings.setValue('filename pattern', QVariant(pattern)) + settings.set('filename pattern', pattern) 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.')) @@ -102,7 +99,7 @@ class ConfigDialog(QDialog, Ui_Dialog): 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())] - settings.setValue('frequently used directories', QVariant(self.directories)) + settings.set('frequently used directories', self.directories) QDialog.accept(self) class Vacuum(QMessageBox): diff --git a/src/calibre/gui2/dialogs/fetch_metadata.py b/src/calibre/gui2/dialogs/fetch_metadata.py index 0967a91bb8..f905e72e81 100644 --- a/src/calibre/gui2/dialogs/fetch_metadata.py +++ b/src/calibre/gui2/dialogs/fetch_metadata.py @@ -12,7 +12,7 @@ from PyQt4.QtGui import QDialog, QItemSelectionModel from calibre.gui2.dialogs.fetch_metadata_ui import Ui_FetchMetadata from calibre.gui2 import error_dialog, NONE, info_dialog -from calibre.ebooks.metadata.isbndb import create_books, option_parser +from calibre.ebooks.metadata.isbndb import create_books, option_parser, ISBNDBError from calibre import Settings class Matches(QAbstractTableModel): @@ -76,7 +76,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata): self.timeout = timeout QObject.connect(self.fetch, SIGNAL('clicked()'), self.fetch_metadata) - self.key.setText(Settings().value('isbndb.com key', QVariant('')).toString()) + self.key.setText(Settings().get('isbndb.com key', '')) self.setWindowTitle(title if title else 'Unknown') self.tlabel.setText(self.tlabel.text().arg(title if title else 'Unknown')) @@ -88,8 +88,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata): self.connect(self.matches, SIGNAL('activated(QModelIndex)'), self.chosen) key = str(self.key.text()) if key: - QTimer.singleShot(100, self.fetch.click) - + QTimer.singleShot(100, self.fetch_metadata) def show_summary(self, current, previous): @@ -106,7 +105,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata): _('You must specify a valid access key for isbndb.com')) return else: - Settings().setValue('isbndb.com key', QVariant(self.key.text())) + Settings().set('isbndb.com key', key) args = ['isbndb'] if self.isbn: @@ -121,36 +120,41 @@ class FetchMetadata(QDialog, Ui_FetchMetadata): self.fetch.setEnabled(False) self.setCursor(Qt.WaitCursor) QCoreApplication.instance().processEvents() - - args.append(key) - parser = option_parser() - opts, args = parser.parse_args(args) - - self.logger = logging.getLogger('Job #'+str(id)) - self.logger.setLevel(logging.DEBUG) - self.log_dest = cStringIO.StringIO() - handler = logging.StreamHandler(self.log_dest) - handler.setLevel(logging.DEBUG) - handler.setFormatter(logging.Formatter('[%(levelname)s] %(filename)s:%(lineno)s: %(message)s')) - self.logger.addHandler(handler) - - books = create_books(opts, args, self.logger, self.timeout) - - self.model = Matches(books) - if self.model.rowCount() < 1: - info_dialog(self, _('No metadata found'), _('No metadata found, try adjusting the title and author or the ISBN key.')).exec_() - self.reject() - - self.matches.setModel(self.model) - QObject.connect(self.matches.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'), - self.show_summary) - self.model.reset() - self.matches.selectionModel().select(self.model.index(0, 0), - QItemSelectionModel.Select | QItemSelectionModel.Rows) - self.matches.setCurrentIndex(self.model.index(0, 0)) - self.fetch.setEnabled(True) - self.unsetCursor() - self.matches.resizeColumnsToContents() + try: + args.append(key) + parser = option_parser() + opts, args = parser.parse_args(args) + + self.logger = logging.getLogger('Job #'+str(id)) + self.logger.setLevel(logging.DEBUG) + self.log_dest = cStringIO.StringIO() + handler = logging.StreamHandler(self.log_dest) + handler.setLevel(logging.DEBUG) + handler.setFormatter(logging.Formatter('[%(levelname)s] %(filename)s:%(lineno)s: %(message)s')) + self.logger.addHandler(handler) + + try: + books = create_books(opts, args, self.logger, self.timeout) + except ISBNDBError, err: + error_dialog(self, _('Error fetching metadata'), str(err)).exec_() + return + + self.model = Matches(books) + if self.model.rowCount() < 1: + info_dialog(self, _('No metadata found'), _('No metadata found, try adjusting the title and author or the ISBN key.')).exec_() + self.reject() + + self.matches.setModel(self.model) + QObject.connect(self.matches.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'), + self.show_summary) + self.model.reset() + self.matches.selectionModel().select(self.model.index(0, 0), + QItemSelectionModel.Select | QItemSelectionModel.Rows) + self.matches.setCurrentIndex(self.model.index(0, 0)) + finally: + self.fetch.setEnabled(True) + self.unsetCursor() + self.matches.resizeColumnsToContents() diff --git a/src/calibre/gui2/dialogs/fetch_metadata.ui b/src/calibre/gui2/dialogs/fetch_metadata.ui index 959fb18d89..e8b4252d8d 100644 --- a/src/calibre/gui2/dialogs/fetch_metadata.ui +++ b/src/calibre/gui2/dialogs/fetch_metadata.ui @@ -47,7 +47,7 @@ - &Access Key; + &Access Key: key diff --git a/src/calibre/gui2/dialogs/lrf_single.py b/src/calibre/gui2/dialogs/lrf_single.py index 1929282770..e5df43a673 100644 --- a/src/calibre/gui2/dialogs/lrf_single.py +++ b/src/calibre/gui2/dialogs/lrf_single.py @@ -106,9 +106,8 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog): def load_saved_global_defaults(self): - cmdline = Settings().value('LRF conversion defaults', QVariant(QByteArray(''))).toByteArray().data() + cmdline = Settings().get('LRF conversion defaults') if cmdline: - cmdline = cPickle.loads(cmdline) self.set_options_from_cmdline(cmdline) def set_options_from_cmdline(self, cmdline): @@ -382,7 +381,7 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog): cmdline.extend([u'--cover', self.cover_file.name]) self.cmdline = [unicode(i) for i in cmdline] else: - Settings().setValue('LRF conversion defaults', QVariant(QByteArray(cPickle.dumps(cmdline)))) + Settings().set('LRF conversion defaults', cmdline) QDialog.accept(self) class LRFBulkDialog(LRFSingleDialog): diff --git a/src/calibre/gui2/dialogs/lrf_single.ui b/src/calibre/gui2/dialogs/lrf_single.ui index cf27b2fd7d..9fd3bee155 100644 --- a/src/calibre/gui2/dialogs/lrf_single.ui +++ b/src/calibre/gui2/dialogs/lrf_single.ui @@ -122,8 +122,8 @@ 0 0 - 622 - 454 + 642 + 458 @@ -435,8 +435,8 @@ 0 0 - 622 - 454 + 642 + 458 @@ -701,8 +701,8 @@ 0 0 - 622 - 454 + 642 + 458 @@ -825,8 +825,8 @@ 0 0 - 622 - 454 + 642 + 458 @@ -923,16 +923,6 @@ - - - - 0 - 0 - 100 - 30 - - - @@ -981,7 +971,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Candara'; font-size:11pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"></p></body></html> diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 26e2058a96..f4fce00482 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -6,7 +6,7 @@ add/remove formats ''' import os -from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt, QVariant +from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog @@ -144,7 +144,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog): self.edit_tags) QObject.connect(self.remove_series_button, SIGNAL('clicked()'), self.remove_unused_series) - self.timeout = float(Settings().value('network timeout', QVariant(5)).toInt()[0]) + self.timeout = float(Settings().get('network timeout', 5)) self.title.setText(db.title(row)) isbn = db.isbn(self.id, index_is_id=True) if not isbn: diff --git a/src/calibre/gui2/dialogs/password.py b/src/calibre/gui2/dialogs/password.py index 2710834a57..9248f313d4 100644 --- a/src/calibre/gui2/dialogs/password.py +++ b/src/calibre/gui2/dialogs/password.py @@ -16,8 +16,8 @@ class PasswordDialog(QDialog, Ui_Dialog): self.setupUi(self) settings = Settings() - un = settings.value(name+': un', QVariant('')).toString() - pw = settings.value(name+': pw', QVariant('')).toString() + un = settings.get(name+': un', u'') + pw = settings.get(name+': pw', u'') self.gui_username.setText(un) self.gui_password.setText(pw) self.sname = name @@ -38,6 +38,6 @@ class PasswordDialog(QDialog, Ui_Dialog): def accept(self): settings = Settings() - settings.setValue(self.sname+': un', QVariant(self.gui_username.text())) - settings.setValue(self.sname+': pw', QVariant(self.gui_password.text())) + settings.set(self.sname+': un', unicode(self.gui_username.text())) + settings.set(self.sname+': pw', unicode(self.gui_password.text())) QDialog.accept(self) diff --git a/src/calibre/gui2/jobs.py b/src/calibre/gui2/jobs.py index da9c9fa36a..d135b7d6cd 100644 --- a/src/calibre/gui2/jobs.py +++ b/src/calibre/gui2/jobs.py @@ -258,8 +258,7 @@ class JobManager(QAbstractTableModel): desc = kwargs.pop('job_description', '') if args and hasattr(args[0], 'append') and '--verbose' not in args[0]: args[0].append('--verbose') - priority = self.PRIORITY[str(Settings().value('conversion job priority', - QVariant('Normal')).toString())] + priority = self.PRIORITY[Settings().get('conversion job priority', 'Normal')] job = self.create_job(ConversionJob, desc, slot, priority, callable, *args, **kwargs) return job.id diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 1130a141c5..5ede2b49a0 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -5,10 +5,11 @@ from datetime import timedelta, datetime from operator import attrgetter from collections import deque from math import cos, sin, pi +from itertools import repeat from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, \ QItemDelegate, QPainterPath, QLinearGradient, QBrush, \ - QPen, QStyle, QPainter, QLineEdit, QApplication, \ - QPalette, QImage + QPen, QStyle, QPainter, QLineEdit, \ + QPalette, QImage, QApplication from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \ QCoreApplication, SIGNAL, QObject, QSize, QModelIndex, \ QTimer @@ -54,9 +55,11 @@ class LibraryDelegate(QItemDelegate): painter.restore() painter.save() + if hasattr(QStyle, 'CE_ItemViewItem'): + QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter) + elif option.state & QStyle.State_Selected: + painter.fillRect(option.rect, option.palette.highlight()) try: - if option.state & QStyle.State_Selected: - painter.fillRect(option.rect, option.palette.highlight()) painter.setRenderHint(QPainter.Antialiasing) y = option.rect.center().y()-self.SIZE/2. x = option.rect.right() - self.SIZE @@ -114,8 +117,7 @@ class BooksModel(QAbstractTableModel): self.load_queue = deque() def read_config(self): - self.use_roman_numbers = bool(Settings().value('use roman numerals for series number', - QVariant(True)).toBool()) + self.use_roman_numbers = Settings().get('use roman numerals for series number', True) def set_database(self, db): @@ -128,6 +130,7 @@ class BooksModel(QAbstractTableModel): def refresh_ids(self, ids, current_row=-1): rows = self.db.refresh_ids(ids) for row in rows: + self.buffer.pop(row, None) if row == current_row: self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), self.get_book_display_info(row)) @@ -594,7 +597,7 @@ class DeviceBooksModel(BooksModel): base = self.map if refinement else self.sorted_map result = [] for i in base: - q = ['', self.db[i].title, self.db[i].authors, '', ', '.join(self.db[i].tags)] + ['' for j in range(10)] + q = ['', self.db[i].title, self.db[i].authors, '', ', '.join(self.db[i].tags)] + list(repeat('', 10)) if OR: add = False for token in tokens: diff --git a/src/calibre/gui2/lrf_renderer/main.py b/src/calibre/gui2/lrf_renderer/main.py index 2d927242cc..edab6b0c1b 100644 --- a/src/calibre/gui2/lrf_renderer/main.py +++ b/src/calibre/gui2/lrf_renderer/main.py @@ -103,13 +103,13 @@ class Main(MainWindow, Ui_MainWindow): def configure(self, triggered): - opts = cPickle.loads(str(Settings().value('ebook viewer options', QVariant(cPickle.dumps(self.opts))).toString())) + opts = Settings().get('LRF ebook viewer options', self.opts) d = Config(self, opts) d.exec_() if d.result() == QDialog.Accepted: opts.white_background = bool(d.white_background.isChecked()) opts.hyphenate = bool(d.hyphenate.isChecked()) - Settings().setValue('ebook viewer options', QVariant(cPickle.dumps(opts))) + Settings().set('LRF ebook viewer options', opts) def set_ebook(self, stream): self.progress_bar.setMinimum(0) @@ -279,8 +279,7 @@ def option_parser(): return parser def normalize_settings(parser, opts): - settings = Settings() - saved_opts = cPickle.loads(str(settings.value('ebook viewer options', QVariant(cPickle.dumps(opts))).toString())) + saved_opts = Settings().get('LRF ebook viewer options', opts) for opt in parser.option_list: if not opt.dest: continue diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 4720cd17c2..06c4eaff19 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -4,7 +4,7 @@ import os, sys, textwrap, collections, traceback, shutil, time from xml.parsers.expat import ExpatError from functools import partial from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \ - QVariant, QThread, QString, QSize, QUrl + QVariant, QThread, QUrl, QSize from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \ QToolButton, QDialog, QDesktopServices from PyQt4.QtSvg import QSvgRenderer @@ -18,7 +18,7 @@ from calibre.devices.interface import Device from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \ initialize_file_icon_provider, question_dialog,\ pixmap_to_data, choose_dir, ORG_NAME, \ - qstring_to_unicode, set_sidebar_directories, \ + set_sidebar_directories, \ SingleApplication, Application, available_height from calibre.gui2.cover_flow import CoverFlow, DatabaseImages from calibre.library.database import LibraryDatabase @@ -979,7 +979,7 @@ class Main(MainWindow, Ui_MainWindow): newloc = self.database_path self.database_path = newloc settings = Settings() - settings.setValue("database path", QVariant(self.database_path)) + settings.set('database path', self.database_path) except Exception, err: traceback.print_exc() d = error_dialog(self, _('Could not move database'), unicode(err)) @@ -1087,13 +1087,11 @@ class Main(MainWindow, Ui_MainWindow): def read_settings(self): settings = Settings() - settings.beginGroup("Main Window") + settings.beginGroup('Main Window') geometry = settings.value('main window geometry', QVariant()).toByteArray() self.restoreGeometry(geometry) settings.endGroup() - dbpath = os.path.join(os.path.expanduser('~'), 'library1.db').decode(sys.getfilesystemencoding()) - self.database_path = qstring_to_unicode(settings.value("database path", - QVariant(QString.fromUtf8(dbpath.encode('utf-8')))).toString()) + self.database_path = settings.get('database path') if not os.access(os.path.dirname(self.database_path), os.W_OK): error_dialog(self, _('Database does not exist'), _('The directory in which the database should be: %s no longer exists. Please choose a new database location.')%self.database_path).exec_() self.database_path = choose_dir(self, 'database path dialog', 'Choose new location for database') @@ -1102,10 +1100,10 @@ class Main(MainWindow, Ui_MainWindow): if not os.path.exists(self.database_path): os.makedirs(self.database_path) self.database_path = os.path.join(self.database_path, 'library1.db') - settings.setValue('database path', QVariant(QString.fromUtf8(self.database_path.encode('utf-8')))) + settings.set('database path', self.database_path) set_sidebar_directories(None) - set_filename_pat(qstring_to_unicode(settings.value('filename pattern', QVariant(get_filename_pat())).toString())) - self.tool_bar.setIconSize(settings.value('toolbar icon size', QVariant(QSize(48, 48))).toSize()) + set_filename_pat(settings.get('filename pattern', get_filename_pat())) + self.tool_bar.setIconSize(settings.get('toolbar icon size', QSize(48, 48))) self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if settings.get('show text in toolbar', True) else Qt.ToolButtonIconOnly) @@ -1192,4 +1190,14 @@ def main(args=sys.argv): if __name__ == '__main__': - sys.exit(main()) + try: + sys.exit(main()) + except: + if not iswindows: raise + from PyQt4.QtGui import QErrorMessage + logfile = os.path.expanduser('~/calibre.log') + if os.path.exists(logfile): + log = open(logfile).read() + if log.strip(): + d = QErrorMessage() + d.showMessage(log) diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index c53f89b3ec..e38b3833f6 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -27,9 +27,9 @@ 0 - 86 + 79 865 - 712 + 716 @@ -44,7 +44,7 @@ - + 0 0 @@ -52,19 +52,25 @@ 10000 - 100 + 110 Qt::ScrollBarAlwaysOff - Qt::ScrollBarAlwaysOff + Qt::ScrollBarAsNeeded + + + true + + + true - 32 - 32 + 40 + 40 @@ -77,14 +83,11 @@ false - 20 + 10 QListView::IconMode - - true - @@ -332,8 +335,8 @@ 0 0 - 847 - 553 + 857 + 552 @@ -380,7 +383,7 @@ 0 0 865 - 86 + 79 @@ -425,9 +428,9 @@ 0 - 798 + 795 865 - 24 + 27 diff --git a/src/calibre/gui2/pictureflow/PyQt/configure.py b/src/calibre/gui2/pictureflow/PyQt/configure.py index 04c7c8fe91..e24e9a8405 100644 --- a/src/calibre/gui2/pictureflow/PyQt/configure.py +++ b/src/calibre/gui2/pictureflow/PyQt/configure.py @@ -1,4 +1,4 @@ -import os, sys, glob +import os, sys, glob, shutil import sipconfig if os.environ.get('PYQT4PATH', None): print os.environ['PYQT4PATH'] @@ -37,7 +37,7 @@ makefile = pyqtconfig.QtGuiModuleMakefile ( # ".dll" extension on Windows). if 'linux' in sys.platform: for f in glob.glob('../../.build/libpictureflow.a'): - os.link(f, './'+os.path.basename(f)) + shutil.copyfile(f, os.path.basename(f)) makefile.extra_lib_dirs = ['.'] else: makefile.extra_lib_dirs = ['..\\..\\.build\\release', '../../.build', '.'] diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 408b8329e5..1f9de46c5b 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -7,9 +7,9 @@ import re, os from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \ QListWidgetItem, QTextCharFormat, QApplication, \ QSyntaxHighlighter, QCursor, QColor, QWidget, \ - QAbstractItemDelegate, QPixmap -from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, QRect, SIGNAL, \ - QObject, QRegExp, QRectF + QAbstractItemDelegate, QPixmap, QStyle, QFontMetrics +from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, SIGNAL, \ + QObject, QRegExp, QString from calibre.gui2.jobs import DetailView from calibre.gui2 import human_readable, NONE, TableView, qstring_to_unicode, error_dialog @@ -123,40 +123,43 @@ class LocationDelegate(QAbstractItemDelegate): def __init__(self): QAbstractItemDelegate.__init__(self) - self.icon_rect = QRect(0, 10, 150, 45) - self.buffer = 5 + self.pixmap = QPixmap(40, 40) + self.text = QString('Reader\n999.9 MB Available202') - def get_rects(self, index, option): - row = index.row() - irect = QRect(self.icon_rect) - irect.translate(row*(irect.width()+self.buffer), 0) - trect = irect.translated(0, irect.height()) - trect.adjust(0, 7, 0, 0) - return irect.adjusted(50, 0, -50, 0), trect + def rects(self, option): + style = QApplication.style() + font = QFont(option.font) + font.setBold(True) + irect = style.itemPixmapRect(option.rect, Qt.AlignHCenter|Qt.AlignTop, self.pixmap) + trect = style.itemTextRect(QFontMetrics(font), option.rect, + Qt.AlignHCenter|Qt.AlignTop, True, self.text) + trect.moveTop(irect.bottom()) + return irect, trect def sizeHint(self, option, index): - irect, trect = self.get_rects(index, option) + irect, trect = self.rects(option) return irect.united(trect).size() def paint(self, painter, option, index): - font = QFont() - font.setPointSize(9) - icon = QIcon(index.model().data(index, Qt.DecorationRole)) - highlight = getattr(index.model(), 'highlight_row', -1) == index.row() - text = index.model().data(index, Qt.DisplayRole).toString() + style = QApplication.style() painter.save() - irect, trect = self.get_rects(index, option) - - mode = QIcon.Normal - if highlight: - font.setBold(True) - mode = QIcon.Active - + if hasattr(QStyle, 'CE_ItemViewItem'): + QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter) + highlight = getattr(index.model(), 'highlight_row', -1) == index.row() + mode = QIcon.Active if highlight else QIcon.Normal + pixmap = QIcon(index.model().data(index, Qt.DecorationRole)).pixmap(self.pixmap.size()) + pixmap = style.generatedIconPixmap(mode, pixmap, option) + text = index.model().data(index, Qt.DisplayRole).toString() + irect, trect = self.rects(option) + style.drawItemPixmap(painter, irect, Qt.AlignHCenter|Qt.AlignTop, pixmap) + font = QFont(option.font) + font.setBold(highlight) painter.setFont(font) - icon.paint(painter, irect, Qt.AlignHCenter|Qt.AlignTop, mode, QIcon.On) - painter.drawText(QRectF(trect), Qt.AlignTop|Qt.AlignHCenter, text) + style.drawItemText(painter, trect, Qt.AlignHCenter|Qt.AlignBottom, + option.palette, True, text) painter.restore() - + + class LocationModel(QAbstractListModel): def __init__(self, parent): QAbstractListModel.__init__(self, parent) diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index b13a7a3680..451e91acc0 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -10,11 +10,10 @@ Command line interface to the calibre database. import sys, os from textwrap import TextWrapper -from PyQt4.QtCore import QVariant - from calibre import OptionParser, Settings, terminal_controller, preferred_encoding from calibre.gui2 import SingleApplication from calibre.ebooks.metadata.meta import get_metadata +from calibre.ebooks.metadata.opf import OPFCreator, OPFReader from calibre.library.database import LibraryDatabase, text_to_tokens FIELDS = set(['title', 'authors', 'publisher', 'rating', 'timestamp', 'size', 'tags', 'comments', 'series', 'series_index', 'formats']) @@ -304,9 +303,64 @@ do nothing. do_remove_format(get_db(dbpath, opts), id, fmt) return 0 +def do_show_metadata(db, id, as_opf): + if not db.has_id(id): + raise ValueError('Id #%d is not present in database.'%id) + mi = db.get_metadata(id, index_is_id=True) + if as_opf: + mi = OPFCreator(os.getcwd(), mi) + mi.render(sys.stdout) + else: + print mi + +def command_show_metadata(args, dbpath): + parser = get_parser(_( +''' +%prog show_metadata [options] id +Show the metadata stored in the calibre database for the book identified by id. +id is an id number from the list command. +''')) + parser.add_option('--as-opf', default=False, action='store_true', + help=_('Print metadata in OPF form (XML)')) + opts, args = parser.parse_args(sys.argv[1:]+args) + if len(args) < 2: + parser.print_help() + print + print _('You must specify an id') + return 1 + id = int(args[1]) + do_show_metadata(get_db(dbpath, opts), id, opts.as_opf) + return 0 + +def do_set_metadata(db, id, stream): + mi = OPFReader(stream) + db.set_metadata(id, mi) + do_show_metadata(db, id, False) + +def command_set_metadata(args, dbpath): + parser = get_parser(_( +''' +%prog set_metadata [options] id /path/to/metadata.opf + +Set the metadata stored in the calibre database for the book identified by id +from the OPF file metadata.opf. id is an id number from the list command. You +can get a quick feel for the OPF format by using the --as-opf switch to the +show_metadata command. +''')) + opts, args = parser.parse_args(sys.argv[1:]+args) + if len(args) < 3: + parser.print_help() + print + print _('You must specify an id and a metadata file') + return 1 + id, opf = int(args[1]), open(args[2], 'rb') + do_set_metadata(get_db(dbpath, opts), id, opf) + return 0 + def main(args=sys.argv): - commands = ('list', 'add', 'remove', 'add_format', 'remove_format') + commands = ('list', 'add', 'remove', 'add_format', 'remove_format', + 'show_metadata', 'set_metadata') parser = OptionParser(_( '''\ %%prog command [options] [arguments] @@ -330,7 +384,7 @@ For help on an individual command: %%prog command --help return 1 command = eval('command_'+args[1]) - dbpath = unicode(Settings().value('database path', QVariant(os.path.expanduser('~/library1.db'))).toString()) + dbpath = Settings().get('database path') return command(args[2:], dbpath) diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py index 580e4a327d..c357239fa5 100644 --- a/src/calibre/library/database.py +++ b/src/calibre/library/database.py @@ -919,13 +919,19 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; def title(self, index, index_is_id=False): if not index_is_id: return self.data[index][1] - return self.conn.execute('SELECT title FROM meta WHERE id=?',(index,)).fetchone()[0] + try: + return self.conn.execute('SELECT title FROM meta WHERE id=?',(index,)).fetchone()[0] + except: + return _('Unknown') def authors(self, index, index_is_id=False): ''' Authors as a comma separated list or None''' if not index_is_id: return self.data[index][2] - return self.conn.execute('SELECT authors FROM meta WHERE id=?',(index,)).fetchone()[0] + try: + return self.conn.execute('SELECT authors FROM meta WHERE id=?',(index,)).fetchone()[0] + except: + pass def isbn(self, idx, index_is_id=False): id = idx if index_is_id else self.id(idx) @@ -937,22 +943,22 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; def publisher(self, index, index_is_id=False): if index_is_id: - return self.conn.execute('SELECT publisher FROM meta WHERE id=?', (id,)).fetchone()[0] + return self.conn.execute('SELECT publisher FROM meta WHERE id=?', (index,)).fetchone()[0] return self.data[index][3] def rating(self, index, index_is_id=False): if index_is_id: - return self.conn.execute('SELECT rating FROM meta WHERE id=?', (id,)).fetchone()[0] + return self.conn.execute('SELECT rating FROM meta WHERE id=?', (index,)).fetchone()[0] return self.data[index][4] def timestamp(self, index, index_is_id=False): if index_is_id: - return self.conn.execute('SELECT timestamp FROM meta WHERE id=?', (id,)).fetchone()[0] + return self.conn.execute('SELECT timestamp FROM meta WHERE id=?', (index,)).fetchone()[0] return self.data[index][5] def max_size(self, index, index_is_id=False): if index_is_id: - return self.conn.execute('SELECT size FROM meta WHERE id=?', (id,)).fetchone()[0] + return self.conn.execute('SELECT size FROM meta WHERE id=?', (index,)).fetchone()[0] return self.data[index][6] def cover(self, index, index_is_id=False): @@ -1259,6 +1265,8 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; ''' Set metadata for the book C{id} from the L{MetaInformation} object C{mi} ''' + if mi.title: + self.set_title(id, mi.title) if not mi.authors: mi.authors = ['Unknown'] authors = [] @@ -1524,6 +1532,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; def has_book(self, mi): return bool(self.conn.execute('SELECT id FROM books where title=?', (mi.title,)).fetchone()) + def has_id(self, id): + return self.conn.execute('SELECT id FROM books where id=?', (id,)).fetchone() is not None + def recursive_import(self, root, single_book_per_directory=True): root = os.path.abspath(root) duplicates = [] diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 34872cbdb3..9a12253b62 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -131,7 +131,7 @@ Why does |app| show only some of my fonts on OS X? The graphical user interface of |app| is not starting on Windows? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you've never used the graphical user interface before, try deleting the file library1.db (it will be somewhere under :file:`C:\\Documents and Settings` on Windows XP and :file:`C:\\Users` on Windows Vista. If that doesn't fix the problem, locate the file libprs500.log (in the same places as library1.db) and post its contents in a help message on the `Forums `_. +If you've never used the graphical user interface before, try deleting the file library1.db (it will be somewhere under :file:`C:\\Documents and Settings` on Windows XP and :file:`C:\\Users` on Windows Vista. If that doesn't fix the problem, locate the file calibre.log (in the same places as library1.db) and post its contents in a help message on the `Forums `_. I want some feature added to |app|. What can I do? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/calibre/translations/bg.po b/src/calibre/translations/bg.po index 35a6df932b..a8d83a4ce0 100644 --- a/src/calibre/translations/bg.po +++ b/src/calibre/translations/bg.po @@ -6,14 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.4.51\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-06-09 20:36+0000\n" +"POT-Creation-Date: 2008-06-12 20:18+0000\n" "PO-Revision-Date: 2008-05-24 06:23+0000\n" "Last-Translator: Kovid Goyal \n" "Language-Team: bg\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2008-06-11 03:04+0000\n" +"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n" "X-Generator: Launchpad (build Unknown)\n" "Generated-By: pygettext.py 1.5\n" @@ -42,8 +42,8 @@ msgid "" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:76 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:255 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:661 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:271 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:677 msgid "Unknown" msgstr "" @@ -66,7 +66,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:86 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:39 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search.py:16 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:385 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:401 msgid "Publisher" msgstr "" @@ -713,9 +713,9 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:26 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:36 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search.py:14 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:252 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:380 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:731 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:268 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:396 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:747 msgid "Title" msgstr "" @@ -724,7 +724,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:512 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:285 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search.py:20 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:238 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:239 msgid "Comments" msgstr "" @@ -889,9 +889,9 @@ msgid "ERROR" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:37 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:257 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:381 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:732 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:273 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:397 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:748 msgid "Author(s)" msgstr "" @@ -967,118 +967,118 @@ msgstr "" msgid "&Stop selected job" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:55 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:285 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:57 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:289 msgid "Metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:56 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:286 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:59 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:290 msgid "Look & Feel" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:57 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:287 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:61 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:291 msgid "Page Setup" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:58 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:288 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:63 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:292 msgid "Chapter Detection" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:85 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:89 msgid "No available formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:86 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:90 msgid "Cannot convert %s as this book has no supported formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:90 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:94 msgid "Choose the format to convert into LRF" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:98 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:102 msgid "Convert %s to LRF" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:101 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:159 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:105 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:161 msgid "Set conversion defaults" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:167 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:171 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:43 msgid "Cannot read" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:168 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:172 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:44 msgid "You do not have permission to read the file: " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:176 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:180 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:52 msgid "Error reading file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:177 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:181 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:53 msgid "

There was an error reading from file:
" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:183 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:187 msgid " is not a valid picture" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:249 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:253 msgid "" "Preprocess the file before converting to LRF. This is useful if you know " "that the file is from a specific source. Known sources:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:250 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:254 msgid "

  1. baen - Books from BAEN Publishers
  2. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:251 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:255 msgid "" "
  3. pdftohtml - HTML files that are the output of the program " "pdftohtml
  4. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:252 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:256 msgid "
  5. book-designer - HTML0 files from Book Designer
  6. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:285 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:289 msgid "" "Specify metadata such as title and author for the book.

    Metadata will be " "updated in the database as well as the generated LRF file." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:286 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:290 msgid "" "Adjust the look of the generated LRF file by specifying things like font " "sizes and the spacing between words." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:287 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:291 msgid "" "Specify the page settings like margins and the screen size of the target " "device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:288 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:292 msgid "Fine tune the detection of chapter and section headings." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:296 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:300 msgid "No help available" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:396 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:400 msgid "Bulk convert ebooks to LRF" msgstr "" @@ -1399,26 +1399,26 @@ msgstr "" msgid "Comma separated list of tags to remove from the books. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:236 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:235 msgid "" "

    Enter your username and password for LibraryThing.com.
    If you " "do not have one, you can register " "for free!.

    " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:266 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:265 msgid "Could not fetch cover.
    " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:266 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:265 msgid "Could not fetch cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:272 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:271 msgid "Cannot fetch cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:272 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:271 msgid "You must specify the ISBN identifier for this book." msgstr "" @@ -1493,13 +1493,13 @@ msgid "Tag" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search.py:18 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:243 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:387 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:244 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:403 msgid "Series" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search.py:19 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:665 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:681 msgid "Format" msgstr "" @@ -1508,7 +1508,7 @@ msgid "Any" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_item_ui.py:35 -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:89 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:96 msgid "Form" msgstr "" @@ -1768,7 +1768,7 @@ msgstr "" msgid "Recipe source code (python)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:90 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:97 msgid "" "

    Set a regular expression pattern to use when trying to guess ebook " "metadata from filenames.

    A )" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:97 -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:100 -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:103 -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:106 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:104 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:107 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:110 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:113 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:116 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:45 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:49 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:54 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:59 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:61 msgid "No match" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:98 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:105 msgid "Authors:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:99 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:106 msgid "Regular expression group name (?P)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:101 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:108 msgid "Series:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:102 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:109 msgid "Regular expression group name (?P)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:104 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:111 msgid "Series index:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:105 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:112 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:115 msgid "Regular expression group name (?P)" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:114 +msgid "ISBN:" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/jobs.py:291 msgid "Job" msgstr "" @@ -1888,54 +1895,54 @@ msgstr "" msgid "Cannot kill waiting jobs." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:227 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:233 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:237 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:228 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:234 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:238 msgid "None" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:228 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:386 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:671 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:735 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:229 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:402 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:687 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:751 msgid "Tags" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:234 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:235 msgid "Formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:243 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:244 msgid "Book %s of %s." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:372 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:388 msgid "Double click to edit me

    " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:382 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:733 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:398 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:749 msgid "Size (MB)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:383 -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:734 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:399 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:750 msgid "Date" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:384 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:400 msgid "Rating" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:666 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:682 msgid "Path" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:670 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:686 msgid "Timestamp" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library.py:770 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:786 msgid "Search (For Advanced Search click the button to the left)" msgstr "" @@ -2007,91 +2014,95 @@ msgstr "" msgid "Configure" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:80 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:82 msgid "Error communicating with device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:93 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:95 msgid "" "

    For help visit %s.kovidgoyal.net
    " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:94 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:96 msgid "%s: %s by Kovid Goyal %%(version)s
    %%(device)s

    " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:112 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:114 msgid "Send to main memory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:113 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:115 msgid "Send to storage card" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:116 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:118 msgid "Edit metadata individually" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:117 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:119 msgid "Edit metadata in bulk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:120 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:122 msgid "Add books from a single directory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:121 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:123 msgid "" "Add books recursively (One book per directory, assumes every ebook file is " "the same book in a different format)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:122 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:124 msgid "" "Add books recursively (Multiple books per directory, assumes every ebook " "file is a different book)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:136 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:138 #: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:274 msgid "Save to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:137 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:139 msgid "Save to disk in a single directory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:140 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:142 #: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:280 msgid "View" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:141 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:143 msgid "View specific format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:156 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:158 msgid "Convert individually" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:157 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:159 msgid "Bulk convert" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:301 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:303 msgid " detected." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:301 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:303 msgid "Device: " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:338 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:328 +msgid "Connected " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:340 msgid "Device database corrupted" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:339 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:341 msgid "" "\n" "

    The database of books on the reader is corrupted. Try the " @@ -2107,42 +2118,42 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:391 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:465 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:393 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:467 msgid "" "

    Books with the same title as the following already exist in the database. " "Add them anyway?