mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Edit book: When right clicking on a class in a HTML file, add an option to rename the class throughout the book
This commit is contained in:
parent
1d6694b04f
commit
4ad268224f
@ -824,6 +824,10 @@ You can also hold down the :kbd:`Ctrl` key and click on any filename inside a li
|
|||||||
to open that file in the editor automatically. Similarly, :kbd:`Ctrl` clicking
|
to open that file in the editor automatically. Similarly, :kbd:`Ctrl` clicking
|
||||||
a class name will take you to the first style rule that matches the tag and class.
|
a class name will take you to the first style rule that matches the tag and class.
|
||||||
|
|
||||||
|
Right clicking a class name in an HTML file will allow you to rename the class,
|
||||||
|
changing all occurrences of the class throughout the book and all its
|
||||||
|
stylesheets.
|
||||||
|
|
||||||
.. _editor_auto_complete:
|
.. _editor_auto_complete:
|
||||||
|
|
||||||
Auto-complete
|
Auto-complete
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
import re
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
@ -463,3 +464,55 @@ def add_stylesheet_links(container, name, text):
|
|||||||
head.append(link)
|
head.append(link)
|
||||||
pretty_xml_tree(head)
|
pretty_xml_tree(head)
|
||||||
return serialize(root, 'text/html')
|
return serialize(root, 'text/html')
|
||||||
|
|
||||||
|
|
||||||
|
def rename_class_in_rule_list(css_rules, old_name, new_name):
|
||||||
|
pat = re.compile(rf'\b{re.escape(old_name)}\b')
|
||||||
|
changed = False
|
||||||
|
for rule in css_rules:
|
||||||
|
if rule.type == rule.STYLE_RULE:
|
||||||
|
old = rule.selectorText
|
||||||
|
q = pat.sub(new_name, old)
|
||||||
|
if q != old:
|
||||||
|
changed = True
|
||||||
|
rule.selectorText = q
|
||||||
|
elif hasattr(rule, 'cssRules'):
|
||||||
|
if rename_class_in_rule_list(rule.cssRules, old_name, new_name):
|
||||||
|
changed = True
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
|
def rename_class_in_doc(container, root, old_name, new_name):
|
||||||
|
changed = False
|
||||||
|
pat = re.compile(rf'\b{re.escape(old_name)}\b')
|
||||||
|
for elem in root.xpath('//*[@class]'):
|
||||||
|
old = elem.get('class')
|
||||||
|
if old:
|
||||||
|
new = pat.sub(new_name, old)
|
||||||
|
if new != old:
|
||||||
|
changed = True
|
||||||
|
elem.set('class', new)
|
||||||
|
for style in root.xpath('//*[local-name()="style"]'):
|
||||||
|
if style.get('type', 'text/css') == 'text/css' and style.text:
|
||||||
|
sheet = container.parse_css(style.text)
|
||||||
|
if rename_class_in_rule_list(sheet.cssRules, old_name, new_name):
|
||||||
|
changed = True
|
||||||
|
style.text = force_unicode(sheet.cssText, 'utf-8')
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
|
def rename_class(container, old_name, new_name):
|
||||||
|
changed = False
|
||||||
|
if not old_name or old_name == new_name:
|
||||||
|
return changed
|
||||||
|
for sheet_name in container.manifest_items_of_type(lambda mt: mt in OEB_STYLES):
|
||||||
|
sheet = container.parsed(sheet_name)
|
||||||
|
if rename_class_in_rule_list(sheet.cssRules, old_name, new_name):
|
||||||
|
container.dirty(sheet_name)
|
||||||
|
changed = True
|
||||||
|
for doc_name in container.manifest_items_of_type(lambda mt: mt in OEB_DOCS):
|
||||||
|
doc = container.parsed(doc_name)
|
||||||
|
if rename_class_in_doc(container, doc, old_name, new_name):
|
||||||
|
container.dirty(doc_name)
|
||||||
|
changed = True
|
||||||
|
return changed
|
||||||
|
@ -25,7 +25,7 @@ from calibre.ebooks.oeb.polish.container import (
|
|||||||
from calibre.ebooks.oeb.polish.cover import (
|
from calibre.ebooks.oeb.polish.cover import (
|
||||||
mark_as_cover, mark_as_titlepage, set_cover
|
mark_as_cover, mark_as_titlepage, set_cover
|
||||||
)
|
)
|
||||||
from calibre.ebooks.oeb.polish.css import filter_css
|
from calibre.ebooks.oeb.polish.css import filter_css, rename_class
|
||||||
from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish
|
from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish
|
||||||
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
|
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
|
||||||
from calibre.ebooks.oeb.polish.replace import (
|
from calibre.ebooks.oeb.polish.replace import (
|
||||||
@ -956,6 +956,22 @@ class Boss(QObject):
|
|||||||
else:
|
else:
|
||||||
ed.action_triggered(action)
|
ed.action_triggered(action)
|
||||||
|
|
||||||
|
def rename_class(self, class_name):
|
||||||
|
self.commit_all_editors_to_container()
|
||||||
|
text, ok = QInputDialog.getText(self.gui, _('New class name'), _(
|
||||||
|
'Rename the class {} to?').format(class_name))
|
||||||
|
if ok:
|
||||||
|
self.add_savepoint(_('Before: Rename {}').format(class_name))
|
||||||
|
with BusyCursor():
|
||||||
|
changed = rename_class(current_container(), class_name, text.strip())
|
||||||
|
if changed:
|
||||||
|
self.apply_container_update_to_gui()
|
||||||
|
self.show_current_diff()
|
||||||
|
else:
|
||||||
|
self.rewind_savepoint()
|
||||||
|
return info_dialog(self.gui, _('No matches'), _(
|
||||||
|
'No class {} found to change').format(class_name), show=True)
|
||||||
|
|
||||||
def set_semantics(self):
|
def set_semantics(self):
|
||||||
self.commit_all_editors_to_container()
|
self.commit_all_editors_to_container()
|
||||||
c = current_container()
|
c = current_container()
|
||||||
@ -1609,6 +1625,8 @@ class Boss(QObject):
|
|||||||
editor.link_clicked.connect(self.editor_link_clicked)
|
editor.link_clicked.connect(self.editor_link_clicked)
|
||||||
if hasattr(editor, 'class_clicked'):
|
if hasattr(editor, 'class_clicked'):
|
||||||
editor.class_clicked.connect(self.editor_class_clicked)
|
editor.class_clicked.connect(self.editor_class_clicked)
|
||||||
|
if hasattr(editor, 'rename_class'):
|
||||||
|
editor.rename_class.connect(self.rename_class)
|
||||||
if getattr(editor, 'syntax', None) == 'html':
|
if getattr(editor, 'syntax', None) == 'html':
|
||||||
editor.smart_highlighting_updated.connect(self.gui.live_css.sync_to_editor)
|
editor.smart_highlighting_updated.connect(self.gui.live_css.sync_to_editor)
|
||||||
if hasattr(editor, 'set_request_completion'):
|
if hasattr(editor, 'set_request_completion'):
|
||||||
|
@ -23,7 +23,8 @@ from calibre.gui2.tweak_book import (
|
|||||||
editors, tprefs, update_mark_text_action
|
editors, tprefs, update_mark_text_action
|
||||||
)
|
)
|
||||||
from calibre.gui2.tweak_book.editor import (
|
from calibre.gui2.tweak_book.editor import (
|
||||||
CSS_PROPERTY, LINK_PROPERTY, SPELL_PROPERTY, TAG_NAME_PROPERTY
|
CLASS_ATTRIBUTE_PROPERTY, CSS_PROPERTY, LINK_PROPERTY, SPELL_PROPERTY,
|
||||||
|
TAG_NAME_PROPERTY
|
||||||
)
|
)
|
||||||
from calibre.gui2.tweak_book.editor.help import help_url
|
from calibre.gui2.tweak_book.editor.help import help_url
|
||||||
from calibre.gui2.tweak_book.editor.text import TextEdit
|
from calibre.gui2.tweak_book.editor.text import TextEdit
|
||||||
@ -142,6 +143,7 @@ class Editor(QMainWindow):
|
|||||||
word_ignored = pyqtSignal(object, object)
|
word_ignored = pyqtSignal(object, object)
|
||||||
link_clicked = pyqtSignal(object)
|
link_clicked = pyqtSignal(object)
|
||||||
class_clicked = pyqtSignal(object)
|
class_clicked = pyqtSignal(object)
|
||||||
|
rename_class = pyqtSignal(object)
|
||||||
smart_highlighting_updated = pyqtSignal()
|
smart_highlighting_updated = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, syntax, parent=None):
|
def __init__(self, syntax, parent=None):
|
||||||
@ -579,6 +581,12 @@ class Editor(QMainWindow):
|
|||||||
href = self.editor.text_for_range(origc.block(), origr)
|
href = self.editor.text_for_range(origc.block(), origr)
|
||||||
m.addAction(_('Open %s') % href, partial(self.link_clicked.emit, href))
|
m.addAction(_('Open %s') % href, partial(self.link_clicked.emit, href))
|
||||||
|
|
||||||
|
if origr is not None and origr.format.property(CLASS_ATTRIBUTE_PROPERTY):
|
||||||
|
cls = self.editor.class_for_position(pos)
|
||||||
|
if cls:
|
||||||
|
class_name = cls['class']
|
||||||
|
m.addAction(_('Rename the class {}').format(class_name), partial(self.rename_class.emit, class_name))
|
||||||
|
|
||||||
if origr is not None and (origr.format.property(TAG_NAME_PROPERTY) or origr.format.property(CSS_PROPERTY)):
|
if origr is not None and (origr.format.property(TAG_NAME_PROPERTY) or origr.format.property(CSS_PROPERTY)):
|
||||||
word = self.editor.text_for_range(origc.block(), origr)
|
word = self.editor.text_for_range(origc.block(), origr)
|
||||||
item_type = 'tag_name' if origr.format.property(TAG_NAME_PROPERTY) else 'css_property'
|
item_type = 'tag_name' if origr.format.property(TAG_NAME_PROPERTY) else 'css_property'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user