mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Edit book: Highlight broken links using an error style
This commit is contained in:
parent
3964715d56
commit
54f774c375
@ -107,3 +107,13 @@ def set_book_locale(lang):
|
||||
dictionaries.default_locale = dictionaries.ui_locale
|
||||
from calibre.gui2.tweak_book.editor.syntax.html import refresh_spell_check_status
|
||||
refresh_spell_check_status()
|
||||
|
||||
def verify_link(url, name=None):
|
||||
if _current_container is None or name is None:
|
||||
return None
|
||||
target = _current_container.href_to_name(url, name)
|
||||
if _current_container.has_name(target):
|
||||
return True
|
||||
if url.partition(':')[0] in {'http', 'https', 'mailto'}:
|
||||
return True
|
||||
return False
|
||||
|
@ -58,9 +58,11 @@ class SimpleUserData(QTextBlockUserData):
|
||||
def __init__(self):
|
||||
QTextBlockUserData.__init__(self)
|
||||
self.state = SimpleState()
|
||||
self.doc_name = None
|
||||
|
||||
def clear(self, state=None):
|
||||
def clear(self, state=None, doc_name=None):
|
||||
self.state = SimpleState() if state is None else state
|
||||
self.doc_name = doc_name
|
||||
|
||||
class SyntaxHighlighter(object):
|
||||
|
||||
@ -71,6 +73,7 @@ class SyntaxHighlighter(object):
|
||||
|
||||
def __init__(self):
|
||||
self.doc = None
|
||||
self.doc_name = None
|
||||
self.requests = deque()
|
||||
self.ignore_requests = False
|
||||
|
||||
@ -86,7 +89,7 @@ class SyntaxHighlighter(object):
|
||||
def create_formats(self):
|
||||
self.formats = self.create_formats_func()
|
||||
|
||||
def set_document(self, doc):
|
||||
def set_document(self, doc, doc_name=None):
|
||||
old_doc = self.doc
|
||||
if old_doc is not None:
|
||||
old_doc.contentsChange.disconnect(self.reformat_blocks)
|
||||
@ -97,9 +100,10 @@ class SyntaxHighlighter(object):
|
||||
blk.layout().clearAdditionalFormats()
|
||||
blk = blk.next()
|
||||
c.endEditBlock()
|
||||
self.doc = None
|
||||
self.doc = self.doc_name = None
|
||||
if doc is not None:
|
||||
self.doc = doc
|
||||
self.doc_name = doc_name
|
||||
doc.contentsChange.connect(self.reformat_blocks)
|
||||
self.rehighlight()
|
||||
|
||||
@ -207,7 +211,7 @@ class SyntaxHighlighter(object):
|
||||
start_state = start_state.state.copy()
|
||||
else:
|
||||
start_state = self.user_data_factory().state
|
||||
ud.clear(state=start_state) # Ensure no stale user data lingers
|
||||
ud.clear(state=start_state, doc_name=self.doc_name) # Ensure no stale user data lingers
|
||||
formats = []
|
||||
for i, num, fmt in run_loop(ud, self.state_map, self.formats, unicode(block.text())):
|
||||
if fmt is not None:
|
||||
|
@ -10,6 +10,7 @@ import re
|
||||
|
||||
from PyQt4.Qt import QTextBlockUserData
|
||||
|
||||
from calibre.gui2.tweak_book import verify_link
|
||||
from calibre.gui2.tweak_book.editor import syntax_text_char_format, LINK_PROPERTY
|
||||
from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter
|
||||
|
||||
@ -158,9 +159,11 @@ class CSSUserData(QTextBlockUserData):
|
||||
def __init__(self):
|
||||
QTextBlockUserData.__init__(self)
|
||||
self.state = CSSState()
|
||||
self.doc_name = None
|
||||
|
||||
def clear(self, state=None):
|
||||
def clear(self, state=None, doc_name=None):
|
||||
self.state = CSSState() if state is None else state
|
||||
self.doc_name = doc_name
|
||||
|
||||
def normal(state, text, i, formats, user_data):
|
||||
' The normal state (outside content blocks {})'
|
||||
@ -220,7 +223,8 @@ def content(state, text, i, formats, user_data):
|
||||
prefix += main[0]
|
||||
suffix = main[-1] + suffix
|
||||
main = main[1:-1]
|
||||
return [(len(prefix), formats[fmt]), (len(main), formats['link']), (len(suffix), formats[fmt])]
|
||||
h = 'bad_link' if verify_link(main, user_data.doc_name) is False else 'link'
|
||||
return [(len(prefix), formats[fmt]), (len(main), formats[h]), (len(suffix), formats[fmt])]
|
||||
return [(len(m.group()), formats[fmt])]
|
||||
|
||||
return [(len(text) - i, formats['unknown-normal'])]
|
||||
@ -282,6 +286,9 @@ def create_formats(highlighter):
|
||||
formats['link'] = syntax_text_char_format(theme['Link'])
|
||||
formats['link'].setToolTip(_('Hold down the Ctrl key and click to open this link'))
|
||||
formats['link'].setProperty(LINK_PROPERTY, True)
|
||||
formats['bad_link'] = syntax_text_char_format(theme['BadLink'])
|
||||
formats['bad_link'].setProperty(LINK_PROPERTY, True)
|
||||
formats['bad_link'].setToolTip(_('This link points to a file that is not present in the book'))
|
||||
return formats
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@ from PyQt4.Qt import QFont, QTextBlockUserData, QTextCharFormat
|
||||
from calibre.ebooks.oeb.polish.spell import html_spell_tags, xml_spell_tags
|
||||
from calibre.spell.dictionary import parse_lang_code
|
||||
from calibre.spell.break_iterator import split_into_words_and_positions
|
||||
from calibre.gui2.tweak_book import dictionaries, tprefs
|
||||
from calibre.gui2.tweak_book import dictionaries, tprefs, verify_link
|
||||
from calibre.gui2.tweak_book.editor import (
|
||||
syntax_text_char_format, SPELL_PROPERTY, SPELL_LOCALE_PROPERTY, store_locale, LINK_PROPERTY)
|
||||
from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter, run_loop
|
||||
@ -219,10 +219,12 @@ class HTMLUserData(QTextBlockUserData):
|
||||
self.attributes = []
|
||||
self.state = State()
|
||||
self.css_user_data = None
|
||||
self.doc_name = None
|
||||
|
||||
def clear(self, state=None):
|
||||
def clear(self, state=None, doc_name=None):
|
||||
self.tags, self.attributes = [], []
|
||||
self.state = State() if state is None else state
|
||||
self.doc_name = doc_name
|
||||
|
||||
@classmethod
|
||||
def tag_ok_for_spell(cls, name):
|
||||
@ -444,6 +446,8 @@ def quoted_val(state, text, i, formats, user_data):
|
||||
is_link = state.attribute_name in LINK_ATTRS
|
||||
|
||||
if is_link:
|
||||
if verify_link(text[i:i+num - 1], user_data.doc_name) is False:
|
||||
return [(num - 1, formats['bad_link']), (1, formats['string'])]
|
||||
return [(num - 1, formats['link']), (1, formats['string'])]
|
||||
return [(num, formats['string'])]
|
||||
|
||||
@ -531,6 +535,9 @@ def create_formats(highlighter, add_css=True):
|
||||
formats['link'] = syntax_text_char_format(t['Link'])
|
||||
formats['link'].setProperty(LINK_PROPERTY, True)
|
||||
formats['link'].setToolTip(_('Hold down the Ctrl key and click to open this link'))
|
||||
formats['bad_link'] = syntax_text_char_format(t['BadLink'])
|
||||
formats['bad_link'].setProperty(LINK_PROPERTY, True)
|
||||
formats['bad_link'].setToolTip(_('This link points to a file that is not present in the book'))
|
||||
return formats
|
||||
|
||||
|
||||
|
@ -209,11 +209,11 @@ class TextEdit(PlainTextEdit):
|
||||
self.highlight_cursor_line()
|
||||
# }}}
|
||||
|
||||
def load_text(self, text, syntax='html', process_template=False):
|
||||
def load_text(self, text, syntax='html', process_template=False, doc_name=None):
|
||||
self.syntax = syntax
|
||||
self.highlighter = get_highlighter(syntax)()
|
||||
self.highlighter.apply_theme(self.theme)
|
||||
self.highlighter.set_document(self.document())
|
||||
self.highlighter.set_document(self.document(), doc_name=doc_name)
|
||||
sclass = {'html':HTMLSmarts, 'xml':HTMLSmarts, 'css':CSSSmarts}.get(syntax, None)
|
||||
if sclass is not None:
|
||||
self.smarts = sclass(self)
|
||||
|
@ -70,6 +70,7 @@ SOLARIZED = \
|
||||
SpellError us=wave uc={orange}
|
||||
Tooltip fg=black bg=ffffed
|
||||
Link fg={blue}
|
||||
BadLink fg={cyan} us=wave uc={red}
|
||||
|
||||
DiffDelete bg={base02} fg={red}
|
||||
DiffInsert bg={base02} fg={green}
|
||||
@ -112,6 +113,7 @@ THEMES = {
|
||||
SpellError us=wave uc=orange
|
||||
SpecialCharacter bg={cursor_loc}
|
||||
Link fg=cyan
|
||||
BadLink fg={string} us=wave uc=red
|
||||
|
||||
DiffDelete bg=341414 fg=642424
|
||||
DiffInsert bg=143414 fg=246424
|
||||
@ -159,6 +161,7 @@ THEMES = {
|
||||
Error us=wave uc=red
|
||||
SpellError us=wave uc=magenta
|
||||
Link fg=blue
|
||||
BadLink fg={string} us=wave uc=red
|
||||
|
||||
DiffDelete bg=rgb(255,180,200) fg=rgb(200,80,110)
|
||||
DiffInsert bg=rgb(180,255,180) fg=rgb(80,210,80)
|
||||
|
@ -17,7 +17,7 @@ from calibre import prints
|
||||
from calibre.constants import DEBUG
|
||||
from calibre.ebooks.chardet import replace_encoding_declarations
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.tweak_book import actions, current_container, tprefs, dictionaries, editor_toolbar_actions
|
||||
from calibre.gui2.tweak_book import actions, current_container, tprefs, dictionaries, editor_toolbar_actions, editor_name
|
||||
from calibre.gui2.tweak_book.editor import SPELL_PROPERTY
|
||||
from calibre.gui2.tweak_book.editor.text import TextEdit
|
||||
from calibre.utils.icu import utf16_length
|
||||
@ -153,11 +153,11 @@ class Editor(QMainWindow):
|
||||
self.data = ans
|
||||
return ans.encode('utf-8')
|
||||
def fset(self, val):
|
||||
self.editor.load_text(val, syntax=self.syntax)
|
||||
self.editor.load_text(val, syntax=self.syntax, doc_name=editor_name(self))
|
||||
return property(fget=fget, fset=fset)
|
||||
|
||||
def init_from_template(self, template):
|
||||
self.editor.load_text(template, syntax=self.syntax, process_template=True)
|
||||
self.editor.load_text(template, syntax=self.syntax, process_template=True, doc_name=editor_name(self))
|
||||
|
||||
def get_raw_data(self):
|
||||
# The EPUB spec requires NFC normalization, see section 1.3.6 of
|
||||
|
Loading…
x
Reference in New Issue
Block a user