Edit book: Highlight broken links using an error style

This commit is contained in:
Kovid Goyal 2014-07-10 09:35:35 +05:30
parent 3964715d56
commit 54f774c375
7 changed files with 44 additions and 13 deletions

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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