mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Edit book: Allow linking of stylesheets into HTML files automatically. Right click ont he selected HTML files in the File browser and choose "Link stylesheets" to have the <link> tags for the shhets automatically inserted.
This commit is contained in:
parent
6d2029b0f2
commit
4c158cb44b
@ -171,6 +171,12 @@ book by clicking :guilabel:`File->New file`. This lets you either import a file
|
||||
by clicking the :guilabel:`Import resource file` button or create a new blank html file
|
||||
or stylesheet by simply entering the file name into the box for the new file.
|
||||
|
||||
Linking stylesheets to HTML files efficiently
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As a convenience, you can select multiple HTML files in the Files Browser,
|
||||
right click and choose Link stylesheets to have |app| automatically insert the
|
||||
<link> tags for those stylesheets into all the selected HTML files.
|
||||
|
||||
Search & Replace
|
||||
-------------------
|
||||
|
@ -37,3 +37,28 @@ class CommentFinder(object):
|
||||
q = bisect(self.starts, offset) - 1
|
||||
return q >= 0 and self.starts[q] <= offset <= self.ends[q]
|
||||
|
||||
def link_stylesheets(container, names, sheets, mtype='text/css'):
|
||||
from calibre.ebooks.oeb.base import XPath, XHTML
|
||||
changed_names = set()
|
||||
snames = set(sheets)
|
||||
lp = XPath('//h:link[@href]')
|
||||
hp = XPath('//h:head')
|
||||
for name in names:
|
||||
root = container.parsed(name)
|
||||
existing = {container.href_to_name(l.get('href'), name) for l in lp(root) if (l.get('type', mtype) or mtype) == mtype}
|
||||
extra = snames - existing
|
||||
if extra:
|
||||
changed_names.add(name)
|
||||
try:
|
||||
parent = hp(root)[0]
|
||||
except (TypeError, IndexError):
|
||||
parent = XHTML('head')
|
||||
container.insert_into_xml(root, parent, index=0)
|
||||
for sheet in sheets:
|
||||
if sheet in extra:
|
||||
container.insert_into_xml(
|
||||
parent, parent.makeelement(XHTML('link'), rel='stylesheet', type=mtype,
|
||||
href=container.name_to_href(sheet, name)))
|
||||
container.dirty(name)
|
||||
|
||||
return changed_names
|
||||
|
@ -24,6 +24,7 @@ from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
|
||||
from calibre.ebooks.oeb.polish.replace import rename_files, replace_file
|
||||
from calibre.ebooks.oeb.polish.split import split, merge, AbortError
|
||||
from calibre.ebooks.oeb.polish.toc import remove_names_from_toc, find_existing_toc
|
||||
from calibre.ebooks.oeb.polish.utils import link_stylesheets
|
||||
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.gui2.tweak_book import set_current_container, current_container, tprefs, actions, editors
|
||||
@ -96,6 +97,7 @@ class Boss(QObject):
|
||||
fl.mark_requested.connect(self.mark_requested)
|
||||
fl.export_requested.connect(self.export_requested)
|
||||
fl.replace_requested.connect(self.replace_requested)
|
||||
fl.link_stylesheets_requested.connect(self.link_stylesheets_requested)
|
||||
self.gui.central.current_editor_changed.connect(self.apply_current_editor_state)
|
||||
self.gui.central.close_requested.connect(self.editor_close_requested)
|
||||
self.gui.central.search_panel.search_triggered.connect(self.search)
|
||||
@ -228,12 +230,13 @@ class Boss(QObject):
|
||||
self.gui.file_list.request_edit(ef)
|
||||
self.gui.toc_view.update_if_visible()
|
||||
|
||||
def update_editors_from_container(self, container=None):
|
||||
def update_editors_from_container(self, container=None, names=None):
|
||||
c = container or current_container()
|
||||
for name, ed in tuple(editors.iteritems()):
|
||||
if c.has_name(name):
|
||||
ed.replace_data(c.raw_data(name))
|
||||
ed.is_synced_to_container = True
|
||||
if names is None or name in names:
|
||||
ed.replace_data(c.raw_data(name))
|
||||
ed.is_synced_to_container = True
|
||||
else:
|
||||
self.close_editor(name)
|
||||
|
||||
@ -830,6 +833,15 @@ class Boss(QObject):
|
||||
if master in editors:
|
||||
self.show_editor(master)
|
||||
|
||||
@in_thread_job
|
||||
def link_stylesheets_requested(self, names, sheets):
|
||||
self.commit_all_editors_to_container()
|
||||
self.add_savepoint(_('Link stylesheets'))
|
||||
changed_names = link_stylesheets(current_container(), names, sheets)
|
||||
if changed_names:
|
||||
self.update_editors_from_container(names=changed_names)
|
||||
self.set_modified()
|
||||
|
||||
@in_thread_job
|
||||
def export_requested(self, name, path):
|
||||
if name in editors and not editors[name].is_synced_to_container:
|
||||
|
@ -16,7 +16,7 @@ from PyQt4.Qt import (
|
||||
QWidget, QTreeWidget, QGridLayout, QSize, Qt, QTreeWidgetItem, QIcon, QFont,
|
||||
QStyledItemDelegate, QStyle, QPixmap, QPainter, pyqtSignal, QMenu, QTimer,
|
||||
QDialogButtonBox, QDialog, QLabel, QLineEdit, QVBoxLayout, QScrollArea,
|
||||
QRadioButton, QFormLayout, QSpinBox)
|
||||
QRadioButton, QFormLayout, QSpinBox, QListWidget, QListWidgetItem)
|
||||
|
||||
from calibre import human_readable, sanitize_file_name_unicode
|
||||
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS
|
||||
@ -120,6 +120,7 @@ class FileList(QTreeWidget):
|
||||
mark_requested = pyqtSignal(object, object)
|
||||
export_requested = pyqtSignal(object, object)
|
||||
replace_requested = pyqtSignal(object, object, object, object)
|
||||
link_stylesheets_requested = pyqtSignal(object, object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QTreeWidget.__init__(self, parent)
|
||||
@ -412,6 +413,9 @@ class FileList(QTreeWidget):
|
||||
for items in selected_map.itervalues():
|
||||
items.sort(key=self.index_of_name)
|
||||
|
||||
if selected_map['text']:
|
||||
m.addAction(QIcon(I('format-text-color.png')), _('Link &stylesheets...'), partial(self.link_stylesheets, selected_map['text']))
|
||||
|
||||
if len(selected_map['text']) > 1:
|
||||
m.addAction(QIcon(I('merge.png')), _('&Merge selected text files'), partial(self.start_merge, 'text', selected_map['text']))
|
||||
if len(selected_map['styles']) > 1:
|
||||
@ -611,6 +615,40 @@ class FileList(QTreeWidget):
|
||||
nname = nname + '.' + ext.lower()
|
||||
self.replace_requested.emit(name, path, nname, force_mt)
|
||||
|
||||
def link_stylesheets(self, names):
|
||||
s = self.categories['styles']
|
||||
sheets = [unicode(s.child(i).data(0, NAME_ROLE).toString()) for i in xrange(s.childCount())]
|
||||
if not sheets:
|
||||
return error_dialog(self, _('No stylesheets'), _(
|
||||
'This book currently has no stylesheets. You must first create a stylesheet'
|
||||
' before linking it.'), show=True)
|
||||
d = QDialog(self)
|
||||
d.l = l = QVBoxLayout(d)
|
||||
d.setLayout(l)
|
||||
d.setWindowTitle(_('Choose stylesheets'))
|
||||
d.la = la = QLabel(_('Choose the stylesheets to link. Drag and drop to re-arrange'))
|
||||
|
||||
la.setWordWrap(True)
|
||||
l.addWidget(la)
|
||||
d.s = s = QListWidget(d)
|
||||
l.addWidget(s)
|
||||
s.setDragEnabled(True)
|
||||
s.setDropIndicatorShown(True)
|
||||
s.setDragDropMode(self.InternalMove)
|
||||
s.setAutoScroll(True)
|
||||
s.setDefaultDropAction(Qt.MoveAction)
|
||||
for name in sheets:
|
||||
i = QListWidgetItem(name, s)
|
||||
flags = Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsDragEnabled | Qt.ItemIsSelectable
|
||||
i.setFlags(flags)
|
||||
i.setCheckState(Qt.Checked)
|
||||
d.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
bb.accepted.connect(d.accept), bb.rejected.connect(d.reject)
|
||||
l.addWidget(bb)
|
||||
if d.exec_() == d.Accepted:
|
||||
sheets = [unicode(s.item(i).text()) for i in xrange(s.count()) if s.item(i).checkState() == Qt.Checked]
|
||||
if sheets:
|
||||
self.link_stylesheets_requested.emit(names, sheets)
|
||||
|
||||
class NewFileDialog(QDialog): # {{{
|
||||
|
||||
@ -729,6 +767,7 @@ class FileListWidget(QWidget):
|
||||
mark_requested = pyqtSignal(object, object)
|
||||
export_requested = pyqtSignal(object, object)
|
||||
replace_requested = pyqtSignal(object, object, object, object)
|
||||
link_stylesheets_requested = pyqtSignal(object, object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
@ -736,10 +775,9 @@ class FileListWidget(QWidget):
|
||||
self.file_list = FileList(self)
|
||||
self.layout().addWidget(self.file_list)
|
||||
self.layout().setContentsMargins(0, 0, 0, 0)
|
||||
for x in ('delete_requested', 'reorder_spine', 'rename_requested',
|
||||
'edit_file', 'merge_requested', 'mark_requested',
|
||||
'export_requested', 'replace_requested', 'bulk_rename_requested'):
|
||||
getattr(self.file_list, x).connect(getattr(self, x))
|
||||
for k, o in vars(self.__class__).iteritems():
|
||||
if isinstance(o, pyqtSignal) and hasattr(self.file_list, k):
|
||||
getattr(self.file_list, k).connect(getattr(self, k))
|
||||
for x in ('delete_done', 'select_name', 'request_edit', 'mark_name_as_current', 'clear_currently_edited_name'):
|
||||
setattr(self, x, getattr(self.file_list, x))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user