diff --git a/src/calibre/gui2/tweak_book/editor/smart/__init__.py b/src/calibre/gui2/tweak_book/editor/smart/__init__.py index 187068af84..471fac2358 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/__init__.py +++ b/src/calibre/gui2/tweak_book/editor/smart/__init__.py @@ -25,3 +25,6 @@ class NullSmarts(object): def goto_sourceline(self, editor, sourceline, tags, attribute=None): return False + + def get_inner_HTML(self, editor): + return None diff --git a/src/calibre/gui2/tweak_book/editor/smart/css.py b/src/calibre/gui2/tweak_book/editor/smart/css.py new file mode 100644 index 0000000000..c51c1dfa55 --- /dev/null +++ b/src/calibre/gui2/tweak_book/editor/smart/css.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2014, Kovid Goyal ' + +from calibre.gui2.tweak_book.editor.smart import NullSmarts + +def find_rule(raw, rule_address): + import tinycss + parser = tinycss.make_parser() + sheet = parser.parse_stylesheet(raw) + rules = sheet.rules + ans = None, None + while rule_address: + try: + r = rules[rule_address[0]] + except IndexError: + return None, None + else: + ans = r.line, r.column + rule_address = rule_address[1:] + if rule_address: + rules = getattr(r, 'rules', ()) + return ans + +class CSSSmarts(NullSmarts): + pass + diff --git a/src/calibre/gui2/tweak_book/editor/smart/html.py b/src/calibre/gui2/tweak_book/editor/smart/html.py index eb1a665139..43319521de 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/html.py +++ b/src/calibre/gui2/tweak_book/editor/smart/html.py @@ -383,3 +383,27 @@ class HTMLSmarts(NullSmarts): editor.setTextCursor(c) return found_tag + def get_inner_HTML(self, editor): + ''' Select the inner HTML of the current tag. Return a cursor with the + inner HTML selected or None. ''' + c = editor.textCursor() + block = c.block() + offset = c.position() - block.position() + nblock, boundary = next_tag_boundary(block, offset) + if boundary.is_start: + # We are within the contents of a tag already + tag = find_closest_containing_tag(block, offset) + else: + # We are inside a tag definition < | > + if boundary.self_closing: + return None # self closing tags have no inner html + tag = find_closest_containing_tag(nblock, boundary.offset + 1) + if tag is None: + return None + ctag = find_closing_tag(tag) + if ctag is None: + return None + c.setPosition(tag.end_block.position() + tag.end_offset + 1) + c.setPosition(ctag.start_block.position() + ctag.start_offset, c.KeepAnchor) + return c + diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 34620924db..c0f2b59cdd 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -25,6 +25,7 @@ from calibre.gui2.tweak_book.editor.syntax.html import HTMLHighlighter, XMLHighl from calibre.gui2.tweak_book.editor.syntax.css import CSSHighlighter from calibre.gui2.tweak_book.editor.smart import NullSmarts from calibre.gui2.tweak_book.editor.smart.html import HTMLSmarts +from calibre.gui2.tweak_book.editor.smart.css import CSSSmarts from calibre.spell.break_iterator import index_of from calibre.utils.icu import safe_chr, string_length @@ -211,7 +212,7 @@ class TextEdit(PlainTextEdit): self.highlighter = get_highlighter(syntax)() self.highlighter.apply_theme(self.theme) self.highlighter.set_document(self.document()) - sclass = {'html':HTMLSmarts, 'xml':HTMLSmarts}.get(syntax, None) + sclass = {'html':HTMLSmarts, 'xml':HTMLSmarts, 'css':CSSSmarts}.get(syntax, None) if sclass is not None: self.smarts = sclass(self) self.setPlainText(unicodedata.normalize('NFC', text)) @@ -755,3 +756,31 @@ class TextEdit(PlainTextEdit): def goto_sourceline(self, sourceline, tags, attribute=None): return self.smarts.goto_sourceline(self, sourceline, tags, attribute=attribute) + def get_tag_contents(self): + c = self.smarts.get_inner_HTML(self) + if c is not None: + return self.selected_text_from_cursor(c) + + def goto_css_rule(self, rule_address, sourceline_address=None): + from calibre.gui2.tweak_book.editor.smart.css import find_rule + block = None + if self.syntax == 'css': + raw = unicode(self.toPlainText()) + line, col = find_rule(raw, rule_address) + if line is not None: + block = self.document().findBlockByNumber(line - 1) + elif sourceline_address is not None: + sourceline, tags = sourceline_address + if self.goto_sourceline(sourceline, tags): + c = self.textCursor() + c.setPosition(c.position() + 1) + self.setTextCursor(c) + raw = self.get_tag_contents() + line, col = find_rule(raw, rule_address) + if line is not None: + block = self.document().findBlockByNumber(c.blockNumber() + line - 1) + + if block is not None and block.isValid(): + c = self.textCursor() + c.setPosition(block.position() + col) + self.setTextCursor(c) diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 9cfe8cb8f8..72bf1a6f04 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -406,6 +406,12 @@ class Editor(QMainWindow): def goto_sourceline(self, *args, **kwargs): return self.editor.goto_sourceline(*args, **kwargs) + def goto_css_rule(self, *args, **kwargs): + return self.editor.goto_css_rule(*args, **kwargs) + + def get_tag_contents(self, *args, **kwargs): + return self.editor.get_tag_contents(*args, **kwargs) + def _nuke_word(self, dic, word, locale): if dic is None: dictionaries.ignore_word(word, locale) diff --git a/src/calibre/gui2/tweak_book/live_css.py b/src/calibre/gui2/tweak_book/live_css.py index 1e6a4777fb..c4a9db0da7 100644 --- a/src/calibre/gui2/tweak_book/live_css.py +++ b/src/calibre/gui2/tweak_book/live_css.py @@ -412,4 +412,8 @@ class LiveCSS(QWidget): if data['type'] == 'inline': sourceline, tags = data['sourceline_address'] editor.goto_sourceline(sourceline, tags, attribute='style') + elif data['type'] == 'sheet': + editor.goto_css_rule(data['rule_address']) + elif data['type'] == 'elem': + editor.goto_css_rule(data['rule_address'], sourceline_address=data['sourceline_address'])