mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-12-16 01:55:02 -05:00
311 lines
13 KiB
Python
311 lines
13 KiB
Python
## Copyright (C) 2007 Kovid Goyal kovid@kovidgoyal.net
|
|
## This program is free software; you can redistribute it and/or modify
|
|
## it under the terms of the GNU General Public License as published by
|
|
## the Free Software Foundation; either version 2 of the License, or
|
|
## (at your option) any later version.
|
|
##
|
|
## This program is distributed in the hope that it will be useful,
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
## GNU General Public License for more details.
|
|
##
|
|
## You should have received a copy of the GNU General Public License along
|
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
import os
|
|
|
|
from PyQt4.QtCore import QObject, SIGNAL, Qt
|
|
from PyQt4.QtGui import QAbstractSpinBox, QLineEdit, QCheckBox, QDialog, QPixmap
|
|
|
|
from libprs500.gui2.dialogs.lrf_single_ui import Ui_LRFSingleDialog
|
|
from libprs500.gui2.dialogs.choose_format import ChooseFormatDialog
|
|
from libprs500.gui2 import qstring_to_unicode, error_dialog, \
|
|
pixmap_to_data, choose_images
|
|
from libprs500.ebooks.lrf import option_parser
|
|
from libprs500.ptempfile import PersistentTemporaryFile
|
|
from libprs500 import __appname__
|
|
|
|
class LRFSingleDialog(QDialog, Ui_LRFSingleDialog):
|
|
|
|
PARSER = option_parser('')
|
|
PREPROCESS_OPTIONS = [ o for o in PARSER.option_groups if o.title == 'PREPROCESSING OPTIONS'][0].option_list
|
|
|
|
@classmethod
|
|
def options(cls):
|
|
options = cls.PARSER.option_list
|
|
for g in cls.PARSER.option_groups:
|
|
options.extend(g.option_list)
|
|
for opt in options:
|
|
yield opt
|
|
|
|
@classmethod
|
|
def option_to_name(cls, opt):
|
|
src = opt.get_opt_string()
|
|
return 'gui_' + src[2:].replace('-', '_')
|
|
|
|
def __init__(self, window, db, row):
|
|
QDialog.__init__(self, window)
|
|
Ui_LRFSingleDialog.__init__(self)
|
|
self.setupUi(self)
|
|
self.categoryList.setCurrentRow(0)
|
|
QObject.connect(self.categoryList, SIGNAL('itemEntered(QListWidgetItem *)'),
|
|
self.show_category_help)
|
|
QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), self.select_cover)
|
|
self.categoryList.leaveEvent = self.reset_help
|
|
self.reset_help()
|
|
self.output_format = 'LRF'
|
|
self.selected_format = None
|
|
self.setup_tooltips()
|
|
self.initialize_options()
|
|
self.db = db
|
|
self.row = row
|
|
self.id = self.db.id(self.row)
|
|
self.cover_changed = False
|
|
self.cpixmap = None
|
|
self.changed = False
|
|
self.read_saved_options()
|
|
self.initialize_metadata()
|
|
formats = [i.upper() for i in self.db.formats(self.row).split(',')]
|
|
try:
|
|
formats.remove(self.output_format)
|
|
except ValueError:
|
|
pass
|
|
if not formats:
|
|
d = error_dialog(window, 'No available formats', 'Cannot convert %s as this book has no supported formats'%(self.gui_title.text()))
|
|
d.exec_()
|
|
|
|
if len(formats) > 1:
|
|
d = ChooseFormatDialog(window, 'Choose the format to convert into LRF', formats)
|
|
d.exec_()
|
|
if d.result() == QDialog.Accepted:
|
|
self.selected_format = d.format()
|
|
elif len(formats) > 0:
|
|
self.selected_format = formats[0]
|
|
|
|
if self.selected_format:
|
|
self.setWindowTitle('Convert %s to LRF'%(self.selected_format,))
|
|
|
|
|
|
|
|
|
|
def read_saved_options(self):
|
|
cmdline = self.db.conversion_options(self.id, self.output_format.lower())
|
|
if cmdline:
|
|
for opt in self.options():
|
|
try:
|
|
i = cmdline.index(opt.get_opt_string())
|
|
except ValueError:
|
|
continue
|
|
guiname = self.option_to_name(opt)
|
|
try:
|
|
obj = getattr(self, guiname)
|
|
except AttributeError:
|
|
continue
|
|
if isinstance(obj, QCheckBox):
|
|
obj.setCheckState(Qt.Checked)
|
|
elif isinstance(obj, QAbstractSpinBox):
|
|
obj.setValue(cmdline[i+1])
|
|
elif isinstance(obj, QLineEdit):
|
|
obj.setText(cmdline[i+1])
|
|
profile = cmdline[cmdline.index('--profile')+1]
|
|
self.gui_profile.setCurrentIndex(self.gui_profile.findText(profile))
|
|
for prepro in self.PREPROCESS_OPTIONS:
|
|
ops = prepro.get_opt_string()
|
|
if ops in cmdline:
|
|
self.preprocess.setCurrentIndex(self.preprocess.findText(ops[2:]))
|
|
break
|
|
|
|
|
|
def select_cover(self, checked):
|
|
files = choose_images(self, 'change cover dialog',
|
|
u'Choose cover for ' + qstring_to_unicode(self.gui_title.text()))
|
|
if not files:
|
|
return
|
|
_file = files[0]
|
|
if _file:
|
|
_file = os.path.abspath(_file)
|
|
if not os.access(_file, os.R_OK):
|
|
d = error_dialog(self.window, 'Cannot read',
|
|
'You do not have permission to read the file: ' + _file)
|
|
d.exec_()
|
|
return
|
|
cf, cover = None, None
|
|
try:
|
|
cf = open(_file, "rb")
|
|
cover = cf.read()
|
|
except IOError, e:
|
|
d = error_dialog(self.window, 'Error reading file',
|
|
"<p>There was an error reading from file: <br /><b>" + _file + "</b></p><br />"+str(e))
|
|
d.exec_()
|
|
if cover:
|
|
pix = QPixmap()
|
|
pix.loadFromData(cover)
|
|
if pix.isNull():
|
|
d = error_dialog(self.window, _file + " is not a valid picture")
|
|
d.exec_()
|
|
else:
|
|
self.cover_path.setText(_file)
|
|
self.cover.setPixmap(pix)
|
|
self.cover_changed = True
|
|
self.cpixmap = pix
|
|
|
|
def initialize_metadata(self):
|
|
db, row = self.db, self.row
|
|
self.id = self.db.id(row)
|
|
self.gui_title.setText(db.title(row))
|
|
au = self.db.authors(row)
|
|
self.gui_author.setText(au if au else '')
|
|
aus = self.db.author_sort(row)
|
|
self.gui_author_sort.setText(aus if aus else '')
|
|
pub = self.db.publisher(row)
|
|
self.gui_publisher.setText(pub if pub else '')
|
|
tags = self.db.tags(row)
|
|
self.tags.setText(tags if tags else '')
|
|
comments = self.db.comments(row)
|
|
self.gui_comment.setPlainText(comments if comments else '')
|
|
|
|
all_series = self.db.all_series()
|
|
series_id = self.db.series_id(row)
|
|
idx, c = None, 0
|
|
for i in all_series:
|
|
id, name = i
|
|
if id == series_id:
|
|
idx = c
|
|
self.series.addItem(name)
|
|
c += 1
|
|
|
|
self.series.lineEdit().setText('')
|
|
if idx is not None:
|
|
self.series.setCurrentIndex(idx)
|
|
cover = self.db.cover(row)
|
|
if cover:
|
|
pm = QPixmap()
|
|
pm.loadFromData(cover)
|
|
if not pm.isNull():
|
|
self.cover.setPixmap(pm)
|
|
|
|
def initialize_options(self):
|
|
'''Initialize non metadata options from the defaults.'''
|
|
for name in self.option_map.keys():
|
|
default = self.option_map[name].default
|
|
obj = getattr(self, name)
|
|
if isinstance(obj, QAbstractSpinBox):
|
|
obj.setValue(default)
|
|
elif isinstance(obj, QLineEdit) and default:
|
|
obj.setText(default)
|
|
elif isinstance(obj, QCheckBox):
|
|
state = Qt.Checked if default else Qt.Unchecked
|
|
obj.setCheckState(state)
|
|
self.gui_headerformat.setDisabled(True)
|
|
self.preprocess.addItem('No preprocessing')
|
|
for opt in self.PREPROCESS_OPTIONS:
|
|
self.preprocess.addItem(opt.get_opt_string()[2:])
|
|
ph = 'Preprocess the file before converting to LRF. This is useful if you know that the file is from a specific source. Known sources:'
|
|
ph += '<ol><li><b>baen</b> - Books from BAEN Publishers</li>'
|
|
ph += '<li><b>pdftohtml</b> - HTML files that are the output of the program pdftohtml</li>'
|
|
self.preprocess.setToolTip(ph)
|
|
self.preprocess.setWhatsThis(ph)
|
|
for profile in self.PARSER.get_option('--profile').choices:
|
|
if self.gui_profile.findText(profile) < 0:
|
|
self.gui_profile.addItem(profile)
|
|
|
|
def setup_tooltips(self):
|
|
def show_item_help(obj, event):
|
|
self.set_help(obj.toolTip())
|
|
|
|
self.option_map = {}
|
|
for opt in self.options():
|
|
try:
|
|
help = opt.help.replace('%default', str(opt.default))
|
|
except (ValueError, TypeError):
|
|
help = opt.help
|
|
|
|
guiname = self.option_to_name(opt)
|
|
if hasattr(self, guiname):
|
|
obj = getattr(self, guiname)
|
|
obj.setToolTip(help)
|
|
obj.setWhatsThis(help)
|
|
self.option_map[guiname] = opt
|
|
obj.__class__.enterEvent = show_item_help
|
|
obj.leaveEvent = self.reset_help
|
|
self.preprocess.__class__.enterEvent = show_item_help
|
|
self.preprocess.leaveEvent = self.reset_help
|
|
|
|
|
|
def show_category_help(self, item):
|
|
text = qstring_to_unicode(item.text())
|
|
help = {
|
|
u'Metadata' : 'Specify metadata such as title and author for the book.<p>Metadata will be updated in the database as well as the generated LRF file.',
|
|
u'Look & Feel' : 'Adjust the look of the generated LRF file by specifying things like font sizes and the spacing between words.',
|
|
u'Page Setup' : 'Specify the page settings like margins and the screen size of the target device.',
|
|
u'Chapter Detection' : 'Fine tune the detection of chapter and section headings.',
|
|
}
|
|
self.set_help(help[text])
|
|
|
|
def set_help(self, msg):
|
|
self.help_view.setHtml('<html><body>%s</body></html>'%(msg,))
|
|
|
|
def reset_help(self, *args):
|
|
self.set_help('<font color="gray">No help available</font>')
|
|
if args:
|
|
args[0].accept()
|
|
|
|
def build_commandline(self):
|
|
cmd = [__appname__]
|
|
for name in self.option_map.keys():
|
|
opt = self.option_map[name].get_opt_string()
|
|
obj = getattr(self, name)
|
|
if isinstance(obj, QAbstractSpinBox):
|
|
cmd.extend([opt, obj.value()])
|
|
elif isinstance(obj, QLineEdit):
|
|
val = qstring_to_unicode(obj.text())
|
|
if val:
|
|
cmd.extend([opt, val])
|
|
elif isinstance(obj, QCheckBox):
|
|
if obj.checkState() == Qt.Checked:
|
|
cmd.append(opt)
|
|
|
|
text = qstring_to_unicode(self.preprocess.currentText())
|
|
if text != 'No preprocessing':
|
|
cmd.append(u'--'+text)
|
|
cmd.extend([u'--profile', qstring_to_unicode(self.gui_profile.currentText())])
|
|
return cmd
|
|
|
|
def title(self):
|
|
return qstring_to_unicode(self.gui_title.text())
|
|
|
|
def write_metadata(self):
|
|
title = qstring_to_unicode(self.gui_title.text())
|
|
self.db.set_title(self.id, title)
|
|
au = qstring_to_unicode(self.gui_author.text()).split(',')
|
|
if au: self.db.set_authors(self.id, au)
|
|
aus = qstring_to_unicode(self.gui_author_sort.text())
|
|
if not aus:
|
|
t = self.db.authors(self.id, index_is_id=True)
|
|
if not t:
|
|
t = 'Unknown'
|
|
aus = t.split(',')[0].strip()
|
|
self.db.set_author_sort(self.id, aus)
|
|
self.db.set_publisher(self.id, qstring_to_unicode(self.gui_publisher.text()))
|
|
self.db.set_tags(self.id, qstring_to_unicode(self.tags.text()).split(','))
|
|
self.db.set_series(self.id, qstring_to_unicode(self.series.currentText()))
|
|
self.db.set_series_index(self.id, self.series_index.value())
|
|
if self.cover_changed:
|
|
self.db.set_cover(self.id, pixmap_to_data(self.cover.pixmap()))
|
|
|
|
|
|
def accept(self):
|
|
cmdline = self.build_commandline()
|
|
self.cover_file = None
|
|
self.write_metadata()
|
|
cover = self.db.cover(self.row)
|
|
if cover:
|
|
self.cover_file = PersistentTemporaryFile(suffix='.jpeg')
|
|
self.cover_file.write(cover)
|
|
self.cover_file.close()
|
|
self.db.set_conversion_options(self.id, self.output_format.lower(), cmdline)
|
|
if self.cover_file:
|
|
cmdline.extend([u'--cover', self.cover_file.name])
|
|
self.cmdline = [unicode(i) for i in cmdline]
|
|
QDialog.accept(self)
|
|
|