mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
FB2 metadata support
This commit is contained in:
commit
afadf5dcac
2
Makefile
2
Makefile
@ -25,7 +25,7 @@ manual:
|
||||
|
||||
pictureflow :
|
||||
mkdir -p src/calibre/plugins && rm -f src/calibre/plugins/*pictureflow* && \
|
||||
cd src/calibre/gui2/pictureflow && \
|
||||
cd src/calibre/gui2/pictureflow && rm *.o && \
|
||||
mkdir -p .build && cd .build && rm -f * && \
|
||||
qmake ../pictureflow-lib.pro && make && \
|
||||
cd ../PyQt && \
|
||||
|
@ -3,7 +3,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
''' Create an OSX installer '''
|
||||
|
||||
import sys, re, os, shutil, subprocess, stat
|
||||
import sys, re, os, shutil, subprocess, stat, glob
|
||||
from setup import VERSION, APPNAME, scripts, main_modules, basenames, main_functions
|
||||
from setuptools import setup
|
||||
from py2app.build_app import py2app
|
||||
@ -132,7 +132,13 @@ _check_symlinks_prescript()
|
||||
fp = '@executable_path/../Frameworks/'
|
||||
print 'Fixing qt dependencies for:', os.path.basename(path)
|
||||
for dep in deps:
|
||||
module = re.search(r'(Qt\w+?)\.framework', dep).group(1)
|
||||
match = re.search(r'(Qt\w+?)\.framework', dep)
|
||||
if not match:
|
||||
match = re.search(r'(phonon)\.framework', dep)
|
||||
if not match:
|
||||
print dep
|
||||
raise Exception('Unknown Qt dependency')
|
||||
module = match.group(1)
|
||||
newpath = fp + '%s.framework/Versions/Current/%s'%(module, module)
|
||||
cmd = ' '.join(['install_name_tool', '-change', dep, newpath, path])
|
||||
subprocess.check_call(cmd, shell=True)
|
||||
@ -156,11 +162,35 @@ _check_symlinks_prescript()
|
||||
|
||||
|
||||
#deps = BuildAPP.qt_dependencies(path)
|
||||
|
||||
def build_plugins(self):
|
||||
cwd = os.getcwd()
|
||||
qmake = '/Users/kovid/qt/bin/qmake'
|
||||
files = []
|
||||
try:
|
||||
print 'Building pictureflow'
|
||||
os.chdir('src/calibre/gui2/pictureflow')
|
||||
for f in glob.glob('*.o'): os.unlink(f)
|
||||
subprocess.check_call([qmake, 'pictureflow-lib.pro'])
|
||||
subprocess.check_call(['make'])
|
||||
files.append((os.path.abspath(os.path.realpath('libpictureflow.dylib')), 'libpictureflow.dylib'))
|
||||
os.chdir('PyQt/.build')
|
||||
subprocess.check_call(['python', '../configure.py'])
|
||||
subprocess.check_call(['make'])
|
||||
files.append((os.path.abspath('pictureflow.so'), 'pictureflow.so'))
|
||||
subprocess.check_call(['install_name_tool', '-change', 'libpictureflow.0.dylib', '@executable_path/../Frameworks/libpictureflow.dylib', 'pictureflow.so'])
|
||||
subprocess.check_call(['install_name_tool', '-change', '/System/Library/Frameworks/Python.framework/Versions/2.5/Python', '@executable_path/../Frameworks/Python.framework/Versions/2.5/Python', 'pictureflow.so'])
|
||||
for i in range(2):
|
||||
deps = BuildAPP.qt_dependencies(files[i][0])
|
||||
BuildAPP.fix_qt_dependencies(files[i][0], deps)
|
||||
|
||||
return files
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def run(self):
|
||||
py2app.run(self)
|
||||
self.add_qt_plugins()
|
||||
resource_dir = os.path.join(self.dist_dir,
|
||||
APPNAME + '.app', 'Contents', 'Resources')
|
||||
frameworks_dir = os.path.join(os.path.dirname(resource_dir), 'Frameworks')
|
||||
@ -178,6 +208,8 @@ _check_symlinks_prescript()
|
||||
f.close()
|
||||
os.chmod(path, stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH|stat.S_IREAD\
|
||||
|stat.S_IWUSR|stat.S_IROTH|stat.S_IRGRP)
|
||||
self.add_qt_plugins()
|
||||
plugin_files = self.build_plugins()
|
||||
|
||||
print
|
||||
print 'Adding clit'
|
||||
@ -188,6 +220,13 @@ _check_symlinks_prescript()
|
||||
print
|
||||
print 'Adding pdftohtml'
|
||||
os.link(os.path.expanduser('~/pdftohtml'), os.path.join(frameworks_dir, 'pdftohtml'))
|
||||
print 'Adding plugins'
|
||||
module_dir = os.path.join(resource_dir, 'lib', 'python2.5', 'lib-dynload')
|
||||
for src, dest in plugin_files:
|
||||
if 'dylib' in dest:
|
||||
os.link(src, os.path.join(frameworks_dir, dest))
|
||||
else:
|
||||
os.link(src, os.path.join(module_dir, dest))
|
||||
print
|
||||
print 'Installing prescipt'
|
||||
sf = [os.path.basename(s) for s in all_names]
|
||||
@ -231,7 +270,8 @@ def main():
|
||||
'argv_emulation' : True,
|
||||
'iconfile' : 'icons/library.icns',
|
||||
'frameworks': ['libusb.dylib', 'libunrar.dylib'],
|
||||
'includes' : ['sip', 'pkg_resources', 'PyQt4.QtSvg',
|
||||
'includes' : ['sip', 'pkg_resources', 'PyQt4.QtXml',
|
||||
'PyQt4.QtSvg',
|
||||
'mechanize', 'ClientForm', 'usbobserver',
|
||||
'genshi', 'calibre.web.feeds.recipes.*',
|
||||
'IPython.Extensions.*', 'pydoc'],
|
||||
@ -251,7 +291,7 @@ def main():
|
||||
setup_requires = ['py2app'],
|
||||
)
|
||||
if auto:
|
||||
subprocess.call(('sudo', 'shutdown', '-h', '+1'))
|
||||
subprocess.call(('sudo', 'shutdown', '-h', '+2'))
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -1,7 +1,7 @@
|
||||
''' E-book management software'''
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__version__ = '0.4.55'
|
||||
__version__ = '0.4.56'
|
||||
__docformat__ = "epytext"
|
||||
__author__ = "Kovid Goyal <kovid at kovidgoyal.net>"
|
||||
__appname__ = 'calibre'
|
||||
|
@ -155,6 +155,7 @@ class BookList(_BookList):
|
||||
src = src.decode('latin1')
|
||||
except UnicodeDecodeError:
|
||||
src = src.decode('cp1252')
|
||||
src = src.replace('<cache:', '<xs1:').replace('</cache:', '</xs1:').replace('xmlns:cache', 'xmlns:xs1')
|
||||
self.document = dom.parseString(src.encode('utf8'))
|
||||
self.root = self.document.documentElement
|
||||
self.prefix = ''
|
||||
|
@ -40,7 +40,7 @@ def xml_to_unicode(raw, verbose=False):
|
||||
return u'', encoding
|
||||
if isinstance(raw, unicode):
|
||||
return raw, encoding
|
||||
match = re.compile('^\s*<\?.*encoding=[\'"](.*?)[\'"].*\?>', re.IGNORECASE).match(raw)
|
||||
match = re.compile(r'<[^<>]+encoding=[\'"](.*?)[\'"][^<>]*>', re.IGNORECASE).search(raw)
|
||||
if match is None:
|
||||
match = re.compile(r'<meta.*?content=[\'"].*?charset=([^\s\'"]+).*?[\'"]', re.IGNORECASE).search(raw)
|
||||
if match is not None:
|
||||
|
@ -287,8 +287,7 @@ class HTMLConverter(object, LoggingInterface):
|
||||
self.book.append(self.current_page)
|
||||
|
||||
for text, tb in self.extra_toc_entries:
|
||||
ascii_text = text.encode('ascii', 'ignore')
|
||||
self.book.addTocEntry(ascii_text, tb)
|
||||
self.book.addTocEntry(text, tb)
|
||||
|
||||
if self.base_font_size > 0:
|
||||
self.log_info('\tRationalizing font sizes...')
|
||||
@ -1427,6 +1426,11 @@ class HTMLConverter(object, LoggingInterface):
|
||||
path = munge_paths(self.target_prefix, tag['href'])[0]
|
||||
ext = os.path.splitext(path)[1]
|
||||
if ext: ext = ext[1:].lower()
|
||||
enc = sys.getfilesystemencoding()
|
||||
if not enc:
|
||||
enc = 'utf8'
|
||||
if isinstance(path, unicode):
|
||||
path = path.encode(enc, 'replace')
|
||||
if os.access(path, os.R_OK) and os.path.isfile(path):
|
||||
if ext in ['png', 'jpg', 'bmp', 'jpeg']:
|
||||
self.process_image(path, tag_css)
|
||||
|
@ -1,6 +1,7 @@
|
||||
<ncx version="2005-1"
|
||||
xml:lang="en"
|
||||
xmlns="http://www.daisy.org/z3986/2005/ncx/"
|
||||
encoding="UTF-8"
|
||||
xmlns:py="http://genshi.edgewall.org/"
|
||||
>
|
||||
<head>
|
||||
|
@ -7,6 +7,7 @@ from urllib import unquote
|
||||
|
||||
from calibre import __appname__
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, BeautifulSoup
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
|
||||
class NCXSoup(BeautifulStoneSoup):
|
||||
|
||||
@ -95,7 +96,7 @@ class TOC(list):
|
||||
|
||||
def read_ncx_toc(self, toc):
|
||||
self.base_path = os.path.dirname(toc)
|
||||
soup = NCXSoup(open(toc, 'rb').read())
|
||||
soup = NCXSoup(xml_to_unicode(open(toc, 'rb').read())[0])
|
||||
|
||||
def process_navpoint(np, dest):
|
||||
play_order = np.get('playOrder', 1)
|
||||
|
@ -5,11 +5,11 @@ import sys, os, re, StringIO, traceback
|
||||
from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, \
|
||||
QByteArray, QLocale, QTranslator, QUrl
|
||||
from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
|
||||
QIcon, QTableView
|
||||
QIcon, QTableView, QDialogButtonBox
|
||||
|
||||
ORG_NAME = 'KovidsBrain'
|
||||
APP_UID = 'libprs500'
|
||||
from calibre import __author__, islinux, iswindows, Settings
|
||||
from calibre import __author__, islinux, iswindows, Settings, isosx
|
||||
|
||||
NONE = QVariant() #: Null value to return from the data function of item models
|
||||
|
||||
@ -339,3 +339,42 @@ def pixmap_to_data(pixmap, format='JPEG'):
|
||||
buf.open(QBuffer.WriteOnly)
|
||||
pixmap.save(buf, format)
|
||||
return str(ba.data())
|
||||
|
||||
class TranslatedDialogButtonBox(QDialogButtonBox):
|
||||
|
||||
STRINGS = {
|
||||
QDialogButtonBox.Ok : (_('&OK'), QDialogButtonBox.AcceptRole),
|
||||
QDialogButtonBox.Open : (_('&Open'), QDialogButtonBox.AcceptRole),
|
||||
QDialogButtonBox.Save : (_('&Save'), QDialogButtonBox.AcceptRole),
|
||||
QDialogButtonBox.Cancel : (_('Cancel'), QDialogButtonBox.RejectRole),
|
||||
QDialogButtonBox.Close : (_('&Close'), QDialogButtonBox.RejectRole),
|
||||
QDialogButtonBox.Discard : (_("&Don't Save") if isosx else _('&Discard'), QDialogButtonBox.DestructiveRole),
|
||||
QDialogButtonBox.Apply : (_('&Apply'), QDialogButtonBox.ApplyRole),
|
||||
QDialogButtonBox.Reset : (_('&Reset'), QDialogButtonBox.ResetRole),
|
||||
QDialogButtonBox.RestoreDefaults : (_('Restore &Defaults'), QDialogButtonBox.ResetRole),
|
||||
QDialogButtonBox.Help : (_('&Help'), QDialogButtonBox.HelpRole),
|
||||
QDialogButtonBox.SaveAll : (_('Save &All'), QDialogButtonBox.AcceptRole),
|
||||
QDialogButtonBox.Yes : (_('&Yes'), QDialogButtonBox.YesRole),
|
||||
QDialogButtonBox.YesToAll : (_('Yes to &All'), QDialogButtonBox.YesRole),
|
||||
QDialogButtonBox.No : (_('&No'), QDialogButtonBox.NoRole),
|
||||
QDialogButtonBox.NoToAll : (_('N&o to All'), QDialogButtonBox.NoRole),
|
||||
QDialogButtonBox.Abort : (_('A&bort'), QDialogButtonBox.RejectRole),
|
||||
QDialogButtonBox.Retry : (_('Re&try'), QDialogButtonBox.AcceptRole),
|
||||
QDialogButtonBox.Ignore : (_('I&gnore'), QDialogButtonBox.AcceptRole),
|
||||
}
|
||||
|
||||
def setStandardButtons(self, buttons):
|
||||
default = False
|
||||
for i in self.STRINGS.keys():
|
||||
if i & buttons:
|
||||
msg, role = self.STRINGS[i]
|
||||
button = self.addButton(msg, role)
|
||||
if not default:
|
||||
default = True
|
||||
button.setDefault(True)
|
||||
|
||||
|
||||
try:
|
||||
from calibre.utils.single_qt_application import SingleApplication
|
||||
except:
|
||||
SingleApplication = None
|
@ -33,11 +33,11 @@ class Matches(QAbstractTableModel):
|
||||
return NONE
|
||||
text = ""
|
||||
if orientation == Qt.Horizontal:
|
||||
if section == 0: text = "Title"
|
||||
elif section == 1: text = "Author(s)"
|
||||
elif section == 2: text = "Author Sort"
|
||||
elif section == 3: text = "Publisher"
|
||||
elif section == 4: text = "ISBN"
|
||||
if section == 0: text = _("Title")
|
||||
elif section == 1: text = _("Author(s)")
|
||||
elif section == 2: text = _("Author Sort")
|
||||
elif section == 3: text = _("Publisher")
|
||||
elif section == 4: text = _("ISBN")
|
||||
|
||||
return QVariant(self.trUtf8(text))
|
||||
else:
|
||||
|
@ -13,6 +13,7 @@ from calibre.gui2 import qstring_to_unicode, error_dialog, question_dialog
|
||||
from calibre.gui2.widgets import PythonHighlighter
|
||||
from calibre.utils import sendmail
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre import isosx
|
||||
|
||||
class UserProfiles(QDialog, Ui_Dialog):
|
||||
|
||||
@ -69,7 +70,7 @@ class UserProfiles(QDialog, Ui_Dialog):
|
||||
pt.close()
|
||||
sendmail(subject='Recipe for '+title,
|
||||
attachments=[pt.name],
|
||||
body='The attached file: %s is a recipe to download %s.'%(os.path.basename(pt.name), title))
|
||||
body=_('Save the text below into a file named recipe.py and send the file to your friends, to allow them to use this recipe.') if isosx else _('The attached file: %s is a recipe to download %s.')%(os.path.basename(pt.name), title))
|
||||
|
||||
|
||||
|
||||
|
@ -298,13 +298,12 @@ class BooksModel(QAbstractTableModel):
|
||||
if col == 0:
|
||||
text = self.db.title(row)
|
||||
if text:
|
||||
return QVariant(BooksView.wrap(text, width=35))
|
||||
return QVariant(text)
|
||||
elif col == 1:
|
||||
au = self.db.authors(row)
|
||||
if au:
|
||||
au = au.split(',')
|
||||
jau = [ BooksView.wrap(a, width=30).strip() for a in au ]
|
||||
return QVariant("\n".join(jau))
|
||||
return QVariant("\n".join(au))
|
||||
elif col == 2:
|
||||
size = self.db.max_size(row)
|
||||
if size:
|
||||
@ -321,7 +320,7 @@ class BooksModel(QAbstractTableModel):
|
||||
elif col == 5:
|
||||
pub = self.db.publisher(row)
|
||||
if pub:
|
||||
return QVariant(BooksView.wrap(pub, 20))
|
||||
return QVariant(pub)
|
||||
elif col == 6:
|
||||
tags = self.db.tags(row)
|
||||
if tags:
|
||||
|
@ -1,7 +1,7 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import os, sys, textwrap, collections, traceback, shutil, time
|
||||
|
||||
from xml.parsers.expat import ExpatError
|
||||
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
|
||||
QVariant, QThread, QString
|
||||
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
|
||||
@ -16,7 +16,8 @@ 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
|
||||
qstring_to_unicode, set_sidebar_directories, \
|
||||
SingleApplication
|
||||
from calibre import iswindows, isosx
|
||||
from calibre.library.database import LibraryDatabase
|
||||
from calibre.gui2.update import CheckForUpdates
|
||||
@ -55,8 +56,13 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
p.end()
|
||||
self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap))
|
||||
|
||||
def __init__(self, parent=None):
|
||||
def __init__(self, single_instance, parent=None):
|
||||
MainWindow.__init__(self, parent)
|
||||
self.single_instance = single_instance
|
||||
if self.single_instance is not None:
|
||||
self.connect(self.single_instance, SIGNAL('message_received(PyQt_PyObject)'),
|
||||
self.another_instance_wants_to_talk)
|
||||
|
||||
Ui_MainWindow.__init__(self)
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle(__appname__)
|
||||
@ -75,6 +81,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.tb_wrapper = textwrap.TextWrapper(width=40)
|
||||
self.device_connected = False
|
||||
self.viewers = collections.deque()
|
||||
|
||||
####################### Location View ########################
|
||||
QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'),
|
||||
self.location_selected)
|
||||
@ -82,7 +89,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.location_view.location_changed)
|
||||
|
||||
####################### Vanity ########################
|
||||
self.vanity_template = qstring_to_unicode(self.vanity.text().arg(__version__)).replace('%2', '%(version)s').replace('%3', '%(device)s')
|
||||
self.vanity_template = _('<p>For help visit <a href="http://%s.kovidgoyal.net/user_manual">%s.kovidgoyal.net</a><br>')%(__appname__, __appname__)
|
||||
self.vanity_template += _('<b>%s</b>: %s by <b>Kovid Goyal %%(version)s</b><br>%%(device)s</p>')%(__appname__, __version__)
|
||||
self.latest_version = ' '
|
||||
self.vanity.setText(self.vanity_template%dict(version=' ', device=' '))
|
||||
self.device_info = ' '
|
||||
@ -198,6 +206,13 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
|
||||
self.news_menu.set_custom_feeds(self.library_view.model().db.get_feeds())
|
||||
|
||||
def another_instance_wants_to_talk(self, msg):
|
||||
if msg.startswith('launched:'):
|
||||
self.setWindowState(self.windowState() & ~Qt.WindowMinimized|Qt.WindowActive)
|
||||
self.show()
|
||||
self.raise_()
|
||||
self.activateWindow()
|
||||
|
||||
|
||||
def current_view(self):
|
||||
'''Convenience method that returns the currently visible view '''
|
||||
@ -258,7 +273,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
Called once metadata has been read for all books on the device.
|
||||
'''
|
||||
if exception:
|
||||
if 'not well-formed' in str(exception):
|
||||
print exception, type(exception)
|
||||
if isinstance(exception, ExpatError):
|
||||
error_dialog(self, _('Device database corrupted'),
|
||||
_('''
|
||||
<p>The database of books on the reader is corrupted. Try the following:
|
||||
@ -701,10 +717,6 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
warning_dialog(self, 'Could not convert some books', msg).exec_()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def set_conversion_defaults(self, checked):
|
||||
d = LRFSingleDialog(self, None, None)
|
||||
d.exec_()
|
||||
@ -1057,13 +1069,18 @@ def main(args=sys.argv):
|
||||
app = QApplication(args)
|
||||
QCoreApplication.setOrganizationName(ORG_NAME)
|
||||
QCoreApplication.setApplicationName(APP_UID)
|
||||
if not singleinstance('mainGUI'):
|
||||
single_instance = None if SingleApplication is None else SingleApplication('calibre GUI')
|
||||
if not singleinstance('calibre GUI'):
|
||||
if single_instance is not None and single_instance.is_running() and \
|
||||
single_instance.send_message('launched:'+''.join(sys.argv)):
|
||||
return 0
|
||||
|
||||
QMessageBox.critical(None, 'Cannot Start '+__appname__,
|
||||
'<p>%s is already running.</p>'%__appname__)
|
||||
return 1
|
||||
initialize_file_icon_provider()
|
||||
try:
|
||||
main = Main()
|
||||
main = Main(single_instance)
|
||||
except DatabaseLocked, err:
|
||||
QMessageBox.critical(None, 'Cannot Start '+__appname__,
|
||||
'<p>Another program is using the database. <br/>Perhaps %s is already running?<br/>If not try deleting the file %s'%(__appname__, err.lock_file_path))
|
||||
|
@ -102,10 +102,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string><html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For help visit <a href="http://__appname__.kovidgoyal.net/user_manual"><span style=" text-decoration: underline; color:#0000ff;">__appname__.kovidgoyal.net</span></a><br /><br /><span style=" font-weight:600;">__appname__</span>: %1 by <span style=" font-weight:600;">Kovid Goyal</span> %2<br />%3</p></body></html></string>
|
||||
<string></string>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::RichText</enum>
|
||||
|
@ -39,7 +39,9 @@ def build_forms(forms):
|
||||
dat = dat.replace('import images_rc', 'from calibre.gui2 import images_rc')
|
||||
dat = dat.replace('from library import', 'from calibre.gui2.library import')
|
||||
dat = dat.replace('from widgets import', 'from calibre.gui2.widgets import')
|
||||
dat += '\nfrom calibre.gui2 import TranslatedDialogButtonBox'
|
||||
dat = re.compile(r'QtGui.QApplication.translate\(.+?,\s+"(.+?)(?<!\\)",.+?\)', re.DOTALL).sub(r'_("\1")', dat)
|
||||
dat = re.compile(r'QtGui.QDialogButtonBox').sub('TranslatedDialogButtonBox', dat)
|
||||
open(compiled_form, 'wb').write(dat)
|
||||
|
||||
|
||||
|
@ -30,8 +30,10 @@ makefile = pyqtconfig.QtGuiModuleMakefile (
|
||||
# Add the library we are wrapping. The name doesn't include any platform
|
||||
# specific prefixes or extensions (e.g. the "lib" prefix on UNIX, or the
|
||||
# ".dll" extension on Windows).
|
||||
makefile.extra_lib_dirs = ['../../.build', '..\\..\\release']
|
||||
makefile.extra_libs = ['pictureflow0' if 'win' in sys.platform else "pictureflow"]
|
||||
makefile.extra_lib_dirs = ['../../.build', '..\\..\\release', '../../']
|
||||
makefile.extra_libs = ['pictureflow0' if 'win' in sys.platform and 'darwin' not in sys.platform else "pictureflow"]
|
||||
makefile.extra_cflags = ['-arch i386', '-arch ppc'] if 'darwin' in sys.platform else []
|
||||
makefile.extra_cxxflags = makefile.extra_cflags
|
||||
if 'linux' in sys.platform:
|
||||
makefile.extra_lflags = ['-Wl,--rpath=.']
|
||||
|
||||
|
@ -9,7 +9,7 @@ from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \
|
||||
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
||||
QAbstractItemDelegate, QStyle
|
||||
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, QRect, SIGNAL, \
|
||||
QObject, QRegExp, QSize, QRectF
|
||||
QObject, QRegExp, QRectF
|
||||
|
||||
from calibre.gui2.jobs import DetailView
|
||||
from calibre.gui2 import human_readable, NONE, TableView, qstring_to_unicode, error_dialog
|
||||
@ -18,6 +18,8 @@ from calibre import fit_image, get_font_families, Settings
|
||||
from calibre.ebooks.metadata.meta import get_filename_pat, metadata_from_filename, \
|
||||
set_filename_pat
|
||||
|
||||
|
||||
|
||||
class FilenamePattern(QWidget, Ui_Form):
|
||||
|
||||
def __init__(self, parent):
|
||||
@ -83,7 +85,7 @@ class LocationDelegate(QAbstractItemDelegate):
|
||||
def __init__(self):
|
||||
QAbstractItemDelegate.__init__(self)
|
||||
self.icon_rect = QRect(0, 10, 150, 45)
|
||||
self.buffer = 0
|
||||
self.buffer = 5
|
||||
|
||||
def get_rects(self, index, option):
|
||||
row = index.row()
|
||||
@ -98,33 +100,21 @@ class LocationDelegate(QAbstractItemDelegate):
|
||||
return irect.united(trect).size()
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
selected = bool(option.state & QStyle.State_Selected)
|
||||
active = bool(option.state & QStyle.State_Active)
|
||||
font = QFont()
|
||||
font.setBold(True)
|
||||
font.setPointSize(8)
|
||||
mode = QIcon.Active if active else QIcon.Selected if selected else QIcon.Normal
|
||||
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()
|
||||
painter.save()
|
||||
irect, trect = self.get_rects(index, option)
|
||||
|
||||
mode = QIcon.Normal
|
||||
if highlight:
|
||||
font.setItalic(True)
|
||||
font.setBold(True)
|
||||
mode = QIcon.Active
|
||||
|
||||
painter.setFont(font)
|
||||
icon.paint(painter, irect, Qt.AlignHCenter|Qt.AlignTop, mode, QIcon.On)
|
||||
if selected:
|
||||
brect = painter.drawText(QRectF(trect), Qt.AlignTop|Qt.AlignHCenter, text)
|
||||
brect.adjust(-3, -0, 3, 0)
|
||||
painter.fillRect(brect, option.palette.highlight())
|
||||
painter.save()
|
||||
painter.setPen(Qt.DotLine)
|
||||
painter.drawRect(brect)
|
||||
painter.restore()
|
||||
painter.setBrush(option.palette.highlightedText())
|
||||
else:
|
||||
painter.setBrush(option.palette.text())
|
||||
|
||||
painter.drawText(QRectF(trect), Qt.AlignTop|Qt.AlignHCenter, text)
|
||||
painter.restore()
|
||||
|
||||
@ -138,7 +128,12 @@ class LocationModel(QAbstractListModel):
|
||||
_('Reader\n%s available'),
|
||||
_('Card\n%s available')]
|
||||
self.free = [-1, -1]
|
||||
self.highlight_row = 0
|
||||
self.highlight_row = 0
|
||||
self.tooltips = [
|
||||
_('Click to see the list of books available on your computer'),
|
||||
_('Click to see the list of books in the main memory of your reader'),
|
||||
_('Click to see the list of books on the storage card in your reader')
|
||||
]
|
||||
|
||||
def rowCount(self, parent):
|
||||
return 1 + sum([1 for i in self.free if i >= 0])
|
||||
@ -152,6 +147,8 @@ class LocationModel(QAbstractListModel):
|
||||
data = QVariant(text)
|
||||
elif role == Qt.DecorationRole:
|
||||
data = self.icons[row]
|
||||
elif role == Qt.ToolTipRole:
|
||||
return QVariant(self.tooltips[row])
|
||||
return data
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
@ -176,7 +173,8 @@ class LocationView(QListView):
|
||||
self.reset()
|
||||
QObject.connect(self.selectionModel(), SIGNAL('currentChanged(QModelIndex, QModelIndex)'), self.current_changed)
|
||||
self.delegate = LocationDelegate()
|
||||
self.setItemDelegate(self.delegate)
|
||||
self.setItemDelegate(self.delegate)
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
def current_changed(self, current, previous):
|
||||
i = current.row()
|
||||
|
2575
src/calibre/translations/ru.po
Normal file
2575
src/calibre/translations/ru.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,10 @@ def sendmail(recipient='', subject='', attachments=[], body=''):
|
||||
pt.write(open(attachments[0], 'rb').read())
|
||||
pt.close()
|
||||
|
||||
subprocess.check_call('open -t '+pt.name, shell=True)
|
||||
try:
|
||||
subprocess.call('open -t '+pt.name, shell=True)
|
||||
except: # For some reason making this call leads to a system call interrupted error
|
||||
pass
|
||||
else:
|
||||
body = '"' + body.replace('"', '\\"') + '"'
|
||||
subject = '"' + subject.replace('"', '\\"') + '"'
|
||||
|
162
src/calibre/utils/single_qt_application.py
Normal file
162
src/calibre/utils/single_qt_application.py
Normal file
@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Enforces running of only a single application instance and allows for messaging between
|
||||
applications using a local socket.
|
||||
'''
|
||||
import atexit
|
||||
|
||||
from PyQt4.QtCore import QByteArray, QDataStream, QIODevice, SIGNAL, QObject, Qt
|
||||
from PyQt4.QtNetwork import QLocalSocket, QLocalServer
|
||||
|
||||
timeout_read = 5000
|
||||
timeout_connect = 500
|
||||
|
||||
def write_message(socket, message, timeout = 5000):
|
||||
block = QByteArray()
|
||||
out = QDataStream(block, QIODevice.WriteOnly)
|
||||
|
||||
out.writeInt32(0)
|
||||
out.writeString(message)
|
||||
out.device().seek(0)
|
||||
out.writeInt32(len(message))
|
||||
|
||||
socket.write(block)
|
||||
|
||||
return getattr(socket, 'state', lambda : None)() == QLocalSocket.ConnectedState and \
|
||||
bool(socket.waitForBytesWritten(timeout))
|
||||
|
||||
def read_message(socket):
|
||||
if getattr(socket, 'state', lambda : None)() != QLocalSocket.ConnectedState:
|
||||
return ''
|
||||
|
||||
while socket.bytesAvailable() < 4:
|
||||
if not socket.waitForReadyRead(timeout_read):
|
||||
return ''
|
||||
|
||||
message = ''
|
||||
ins = QDataStream(socket)
|
||||
block_size = ins.readInt32()
|
||||
while socket.bytesAvailable() < block_size:
|
||||
if not socket.waitForReadyRead(timeout_read):
|
||||
return message
|
||||
return str(ins.readString())
|
||||
|
||||
class Connection(QObject):
|
||||
|
||||
def __init__(self, socket, name):
|
||||
QObject.__init__(self)
|
||||
self.socket = socket
|
||||
self.name = name
|
||||
self.magic = self.name + ':'
|
||||
self.connect(self.socket, SIGNAL('readyRead()'), self.read_msg, Qt.QueuedConnection)
|
||||
self.write_succeeded = write_message(self.socket, self.name)
|
||||
self.connect(self.socket, SIGNAL('disconnected()'), self.disconnected)
|
||||
if not self.write_succeeded:
|
||||
self.socket.abort()
|
||||
|
||||
def read_msg(self):
|
||||
while self.socket.bytesAvailable() > 0:
|
||||
msg = read_message(self.socket)
|
||||
if msg.startswith(self.magic):
|
||||
self.emit(SIGNAL('message_received(PyQt_PyObject)'), msg[len(self.magic):])
|
||||
|
||||
def disconnected(self):
|
||||
self.emit(SIGNAL('disconnected()'))
|
||||
|
||||
|
||||
class LocalServer(QLocalServer):
|
||||
|
||||
def __init__(self, server_id, parent=None):
|
||||
QLocalServer.__init__(self, parent)
|
||||
self.server_id = str(server_id)
|
||||
self.mr = lambda x : self.emit(SIGNAL('message_received(PyQt_PyObject)'), x)
|
||||
self.connections = []
|
||||
self.connect(self, SIGNAL('newConnection()'), self.new_connection)
|
||||
|
||||
def new_connection(self):
|
||||
socket = self.nextPendingConnection()
|
||||
conn = Connection(socket, self.server_id)
|
||||
if conn.socket.state() != QLocalSocket.UnconnectedState:
|
||||
self.connect(conn, SIGNAL('message_received(PyQt_PyObject)'), self.mr)
|
||||
self.connect(conn, SIGNAL('disconnected()'), self.free)
|
||||
self.connections.append(conn)
|
||||
|
||||
def free(self):
|
||||
pop = []
|
||||
for conn in self.connections:
|
||||
if conn.socket.state() == QLocalSocket.UnconnectedState:
|
||||
pop.append(conn)
|
||||
|
||||
for conn in pop:
|
||||
self.connections.remove(conn)
|
||||
|
||||
|
||||
|
||||
class SingleApplication(QObject):
|
||||
|
||||
def __init__(self, name, parent=None, server_name='calibre_server'):
|
||||
QObject.__init__(self, parent)
|
||||
self.name = name
|
||||
self.server_name = server_name
|
||||
self.running = False
|
||||
self.mr = lambda x : self.emit(SIGNAL('message_received(PyQt_PyObject)'), x)
|
||||
|
||||
# Check if server is already running
|
||||
self.socket = QLocalSocket(self)
|
||||
self.socket.connectToServer(self.server_name)
|
||||
if self.socket.waitForConnected(timeout_connect):
|
||||
msg = read_message(self.socket)
|
||||
if msg == self.name:
|
||||
self.running = True
|
||||
|
||||
|
||||
# Start server
|
||||
self.server = None
|
||||
if not self.running:
|
||||
self.socket.abort()
|
||||
self.socket = None
|
||||
self.server = LocalServer(self.name, self)
|
||||
self.connect(self.server, SIGNAL('message_received(PyQt_PyObject)'),
|
||||
self.mr, Qt.QueuedConnection)
|
||||
|
||||
if not self.server.listen(self.server_name):
|
||||
if not self.server.listen(self.server_name):
|
||||
self.server = None
|
||||
if self.server is not None:
|
||||
atexit.register(self.server.close)
|
||||
|
||||
|
||||
def is_running(self, name=None):
|
||||
return self.running if name is None else SingleApplication().is_running()
|
||||
|
||||
def send_message(self, msg, timeout=3000):
|
||||
return self.running and write_message(self.socket, self.name+':'+msg, timeout)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt4.Qt import QWidget, QApplication
|
||||
class Test(QWidget):
|
||||
|
||||
def __init__(self, sa):
|
||||
QWidget.__init__(self)
|
||||
self.sa = sa
|
||||
self.connect(sa, SIGNAL('message_received(PyQt_PyObject)'), self.mr)
|
||||
|
||||
def mr(self, msg):
|
||||
print 'Message received:', msg
|
||||
|
||||
app = QApplication([])
|
||||
app.connect(app, SIGNAL('lastWindowClosed()'), app.quit)
|
||||
sa = SingleApplication('test SA')
|
||||
if sa.is_running():
|
||||
sa.send_message('test message')
|
||||
else:
|
||||
widget = Test(sa)
|
||||
widget.show()
|
||||
app.exec_()
|
||||
|
||||
|
||||
|
@ -62,9 +62,9 @@ def compile_recipe(src):
|
||||
enc = match.group(1) if match else 'utf-8'
|
||||
src = src.decode(enc)
|
||||
f = open(temp, 'wb')
|
||||
src = '# coding=utf-8\n' + src
|
||||
src = 'from %s.web.feeds.news import BasicNewsRecipe, AutomaticNewsRecipe\n'%__appname__ + src
|
||||
src = 'from %s.ebooks.lrf.web.profiles import DefaultProfile, FullContentProfile\n'%__appname__ + src
|
||||
src = '# coding: utf-8\n' + src
|
||||
f.write(src.replace('from libprs500', 'from calibre').encode('utf-8'))
|
||||
f.close()
|
||||
module = imp.find_module(temp.namebase, [temp.dirname()])
|
||||
|
@ -250,7 +250,10 @@ class RecursiveFetcher(object, LoggingInterface):
|
||||
self.log_debug('Error: %s', str(err), exc_info=True)
|
||||
continue
|
||||
c += 1
|
||||
imgpath = os.path.join(diskpath, sanitize_file_name('img'+str(c)+ext))
|
||||
fname = sanitize_file_name('img'+str(c)+ext)
|
||||
if isinstance(fname, unicode):
|
||||
fname = fname.encode('ascii', 'replace')
|
||||
imgpath = os.path.join(diskpath, fname)
|
||||
with self.imagemap_lock:
|
||||
self.imagemap[iurl] = imgpath
|
||||
open(imgpath, 'wb').write(f.read())
|
||||
|
Loading…
x
Reference in New Issue
Block a user