Edit book: When right clicking on HTML files in EPUB 3 books, allow marking them as the Table of Contents (NAV document). Fixes #1990507 [[Editor][Request]: Context menu entry to "Mark as NAV"](https://bugs.launchpad.net/calibre/+bug/1990507)

This commit is contained in:
Kovid Goyal 2022-09-26 19:37:28 +05:30
parent 19cb5117f0
commit 45db1ac43c
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 55 additions and 20 deletions

View File

@ -975,18 +975,26 @@ def read_raster_cover(root, prefixes, refines):
return href
def ensure_is_only_raster_cover(root, prefixes, refines, raster_cover_item_href):
for item in XPath('./opf:metadata/opf:meta[@name="cover"]')(root):
remove_element(item, refines)
for item in items_with_property(root, 'cover-image', prefixes):
prop = normalize_whitespace(item.get('properties').replace('cover-image', ''))
def set_unique_property(property_name, root, prefixes, href):
changed = False
for item in items_with_property(root, property_name, prefixes):
prop = normalize_whitespace(item.get('properties').replace(property_name, ''))
changed = True
if prop:
item.set('properties', prop)
else:
del item.attrib['properties']
for item in XPath('./opf:manifest/opf:item')(root):
if item.get('href') == raster_cover_item_href:
item.set('properties', normalize_whitespace((item.get('properties') or '') + ' cover-image'))
if item.get('href') == href:
changed = True
item.set('properties', normalize_whitespace((item.get('properties') or '') + f' {property_name}'))
return changed
def ensure_is_only_raster_cover(root, prefixes, refines, raster_cover_item_href):
for item in XPath('./opf:metadata/opf:meta[@name="cover"]')(root):
remove_element(item, refines)
set_unique_property('cover-image', root, prefixes, raster_cover_item_href)
# }}}

View File

@ -280,6 +280,15 @@ def find_existing_nav_toc(container):
return name
def mark_as_nav(container, name):
from calibre.ebooks.metadata.opf3 import read_prefixes, set_unique_property
if container.opf_version_parsed.major > 2:
prefixes = read_prefixes(container.opf)
href = container.href_to_name(name, container.opf_name)
if set_unique_property('nav', container.opf, prefixes, href):
container.dirty(container.opf_name)
def get_x_toc(container, find_toc, parse_toc, verify_destinations=True):
def empty_toc():
ans = TOC()

View File

@ -8,7 +8,6 @@ import shutil
import sys
import tempfile
from functools import partial, wraps
from qt.core import (
QApplication, QCheckBox, QDialog, QDialogButtonBox, QGridLayout, QIcon,
QInputDialog, QLabel, QMimeData, QObject, QSize, Qt, QTimer, QUrl, QVBoxLayout,
@ -31,7 +30,9 @@ from calibre.ebooks.oeb.polish.replace import (
get_recommended_folders, rationalize_folders, rename_files, replace_file
)
from calibre.ebooks.oeb.polish.split import AbortError, merge, multisplit, split
from calibre.ebooks.oeb.polish.toc import create_inline_toc, remove_names_from_toc
from calibre.ebooks.oeb.polish.toc import (
create_inline_toc, mark_as_nav, remove_names_from_toc
)
from calibre.ebooks.oeb.polish.utils import (
link_stylesheets, setup_css_parser_serialization as scs
)
@ -60,12 +61,12 @@ from calibre.gui2.tweak_book.spell import (
from calibre.gui2.tweak_book.toc import TOCEditor
from calibre.gui2.tweak_book.undo import GlobalUndoHistory
from calibre.gui2.tweak_book.widgets import (
AddCover, FilterCSS, ImportForeign, InsertLink, InsertSemantics,
InsertTag, MultiSplit, QuickOpen, RationalizeFolders
AddCover, FilterCSS, ImportForeign, InsertLink, InsertSemantics, InsertTag,
MultiSplit, QuickOpen, RationalizeFolders
)
from calibre.gui2.widgets import BusyCursor
from calibre.ptempfile import PersistentTemporaryDirectory, TemporaryDirectory
from calibre.utils.config import JSONConfig
from calibre.gui2.widgets import BusyCursor
from calibre.utils.icu import numeric_sort_key
from calibre.utils.imghdr import identify
from calibre.utils.tdir_in_cache import tdir_in_cache
@ -213,7 +214,9 @@ class Boss(QObject):
for ed in itervalues(editors):
ed.apply_settings(dictionaries_changed=p.dictionaries_changed)
if orig_spell != tprefs['inline_spell_check']:
from calibre.gui2.tweak_book.editor.syntax.html import refresh_spell_check_status
from calibre.gui2.tweak_book.editor.syntax.html import (
refresh_spell_check_status
)
refresh_spell_check_status()
for ed in itervalues(editors):
try:
@ -230,6 +233,8 @@ class Boss(QObject):
action, move_to_start = action.partition(':')[0::2]
move_to_start = move_to_start == 'True'
mark_as_titlepage(current_container(), name, move_to_start=move_to_start)
elif action == 'nav':
mark_as_nav(current_container(), name)
if c.opf_name in editors:
editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
@ -490,7 +495,9 @@ class Boss(QObject):
return
added_name = self.do_add_file(d.file_name, d.file_data, using_template=d.using_template, edit_file=True)
if d.file_name.rpartition('.')[2].lower() in ('ttf', 'otf', 'woff'):
from calibre.gui2.tweak_book.manage_fonts import show_font_face_rule_for_font_file
from calibre.gui2.tweak_book.manage_fonts import (
show_font_face_rule_for_font_file
)
show_font_face_rule_for_font_file(d.file_data, added_name, self.gui)
def do_add_file(self, file_name, data, using_template=False, edit_file=False):
@ -553,7 +560,9 @@ class Boss(QObject):
self.set_modified()
completion_worker().clear_caches('names')
if added_fonts:
from calibre.gui2.tweak_book.manage_fonts import show_font_face_rule_for_font_files
from calibre.gui2.tweak_book.manage_fonts import (
show_font_face_rule_for_font_files
)
show_font_face_rule_for_font_files(c, added_fonts, self.gui)
def add_cover(self):
@ -633,8 +642,8 @@ class Boss(QObject):
global last_used_html_transform_rules
if not self.ensure_book(_('You must first open a book in order to transform styles.')):
return
from calibre.gui2.html_transform_rules import RulesDialog
from calibre.ebooks.html_transform_rules import transform_container
from calibre.gui2.html_transform_rules import RulesDialog
d = RulesDialog(self.gui)
d.rules = last_used_html_transform_rules
d.transform_scope = tprefs['html_transform_scope']
@ -677,8 +686,8 @@ class Boss(QObject):
global last_used_transform_rules
if not self.ensure_book(_('You must first open a book in order to transform styles.')):
return
from calibre.gui2.css_transform_rules import RulesDialog
from calibre.ebooks.css_transform_rules import transform_container
from calibre.gui2.css_transform_rules import RulesDialog
d = RulesDialog(self.gui)
d.rules = last_used_transform_rules
ret = d.exec()
@ -1203,7 +1212,9 @@ class Boss(QObject):
'No file with the name %s was found in the book') % target, show=True)
def editor_class_clicked(self, class_data):
from calibre.gui2.tweak_book.jump_to_class import find_first_matching_rule, NoMatchingTagFound, NoMatchingRuleFound
from calibre.gui2.tweak_book.jump_to_class import (
NoMatchingRuleFound, NoMatchingTagFound, find_first_matching_rule
)
ed = self.gui.central.current_editor
name = editor_name(ed)
try:
@ -1594,7 +1605,9 @@ class Boss(QObject):
def compress_images(self):
if not self.ensure_book(_('You must first open a book in order to compress images.')):
return
from calibre.gui2.tweak_book.polish import show_report, CompressImages, CompressImagesProgress
from calibre.gui2.tweak_book.polish import (
CompressImages, CompressImagesProgress, show_report
)
d = CompressImages(self.gui)
if d.exec() == QDialog.DialogCode.Accepted:
with BusyCursor():
@ -1650,8 +1663,8 @@ class Boss(QObject):
editor = self.edit_file(name, 'html')
if not editor or not editor.has_line_numbers:
return False
from calibre.ebooks.oeb.polish.parsing import parse
from calibre.ebooks.epub.cfi.parse import decode_cfi
from calibre.ebooks.oeb.polish.parsing import parse
root = parse(
editor.get_raw_data(), decoder=lambda x: x.decode('utf-8'),
line_numbers=True, linenumber_attribute='data-lnum')

View File

@ -705,6 +705,8 @@ class FileList(QTreeWidget, OpenWithHandler):
m.addAction(QIcon.ic('default_cover.png'), _('Mark %s as cover image') % n, partial(self.mark_as_cover, cn))
elif current_container().SUPPORTS_TITLEPAGES and mt in OEB_DOCS and cat == 'text':
m.addAction(QIcon.ic('default_cover.png'), _('Mark %s as cover page') % n, partial(self.mark_as_titlepage, cn))
if mt in OEB_DOCS and cat in ('text', 'misc') and current_container().opf_version_parsed.major > 2:
m.addAction(QIcon.ic('toc.png'), _('Mark %s as Table of Contents') % n, partial(self.mark_as_nav, cn))
m.addSeparator()
if num > 0:
@ -791,6 +793,9 @@ class FileList(QTreeWidget, OpenWithHandler):
)
self.mark_requested.emit(name, 'titlepage:%r' % move_to_start)
def mark_as_nav(self, name):
self.mark_requested.emit(name, 'nav')
def keyPressEvent(self, ev):
if ev.key() in (Qt.Key.Key_Delete, Qt.Key.Key_Backspace):
ev.accept()