From 062d38a1564b45722cf3d4d5fb4a8d48ab13e867 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 21 May 2014 08:25:55 +0530 Subject: [PATCH] Implement navigation to inline style attributes --- .../gui2/tweak_book/editor/smart/__init__.py | 2 + .../gui2/tweak_book/editor/smart/html.py | 50 ++++++++++++++++++- src/calibre/gui2/tweak_book/editor/text.py | 3 ++ src/calibre/gui2/tweak_book/editor/widget.py | 3 ++ src/calibre/gui2/tweak_book/live_css.py | 8 +-- 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/tweak_book/editor/smart/__init__.py b/src/calibre/gui2/tweak_book/editor/smart/__init__.py index 7b5352d78c3d..187068af84b5 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/__init__.py +++ b/src/calibre/gui2/tweak_book/editor/smart/__init__.py @@ -23,3 +23,5 @@ def verify_for_spellcheck(self, cursor, highlighter): def cursor_position_with_sourceline(self, cursor): return None, None + def goto_sourceline(self, editor, sourceline, tags, attribute=None): + return False diff --git a/src/calibre/gui2/tweak_book/editor/smart/html.py b/src/calibre/gui2/tweak_book/editor/smart/html.py index 1bd6116b8311..eb1a6651390b 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/html.py +++ b/src/calibre/gui2/tweak_book/editor/smart/html.py @@ -14,7 +14,7 @@ from calibre import prepare_string_for_xml from calibre.gui2 import error_dialog -from calibre.gui2.tweak_book.editor.syntax.html import ATTR_NAME, ATTR_END +from calibre.gui2.tweak_book.editor.syntax.html import ATTR_NAME, ATTR_END, ATTR_START, ATTR_VALUE get_offset = itemgetter(0) PARAGRAPH_SEPARATOR = '\u2029' @@ -117,6 +117,28 @@ def find_containing_attribute(block, offset): return boundary.data return None +def find_attribute_in_tag(block, offset, attr_name): + end_block, boundary = next_tag_boundary(block, offset) + if boundary.is_start: + return None, None + end_offset = boundary.offset + end_pos = (end_block.blockNumber(), end_offset) + current_block, current_offset = block, offset + found_attr = False + while True: + current_block, boundary = next_attr_boundary(current_block, current_offset) + if current_block is None or (current_block.blockNumber(), boundary.offset) > end_pos: + return None, None + current_offset = boundary.offset + if found_attr: + if boundary.type is not ATTR_VALUE or boundary.data is not ATTR_START: + return None, None + return current_block, current_offset + else: + if boundary.type is ATTR_NAME and boundary.data.lower() == attr_name.lower(): + found_attr = True + current_offset += 1 + def find_closing_tag(tag, max_tags=sys.maxint): ''' Find the closing tag corresponding to the specified tag. To find it we search for the first closing tag after the specified tag that does not @@ -335,3 +357,29 @@ def cursor_position_with_sourceline(self, cursor): return None, None all_tags = [t.name for t in ud.tags if (t.is_start and not t.closing and t.offset <= start_offset)] return sourceline, all_tags + + def goto_sourceline(self, editor, sourceline, tags, attribute=None): + ''' Move the cursor to the tag identified by sourceline and tags (a + list of tags names on the specified line). If attribute is specified + the cursor will be placed at the start of the attribute value. ''' + block = editor.document().findBlockByNumber(sourceline - 1) # blockNumber() is zero based + found_tag = False + if not block.isValid(): + return found_tag + c = editor.textCursor() + ud = block.userData() + all_tags = [] if ud is None else [t for t in ud.tags if (t.is_start and not t.closing)] + tag_names = [t.name for t in all_tags] + if tag_names[:len(tags)] == tags: + c.setPosition(block.position() + all_tags[len(tags)-1].offset) + found_tag = True + else: + c.setPosition(block.position()) + if found_tag and attribute is not None: + start_offset = c.position() - block.position() + nblock, offset = find_attribute_in_tag(block, start_offset, attribute) + if nblock is not None: + c.setPosition(nblock.position() + offset) + editor.setTextCursor(c) + return found_tag + diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index a7ab7c02c64d..34620924db6c 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -752,3 +752,6 @@ def rename_block_tag(self, new_name): def current_tag(self): return self.smarts.cursor_position_with_sourceline(self.textCursor()) + def goto_sourceline(self, sourceline, tags, attribute=None): + return self.smarts.goto_sourceline(self, sourceline, tags, attribute=attribute) + diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index a60e1418a86b..9cfe8cb8f892 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -403,6 +403,9 @@ def show_context_menu(self, pos): m.addAction(actions['multisplit']) m.exec_(self.editor.mapToGlobal(pos)) + def goto_sourceline(self, *args, **kwargs): + return self.editor.goto_sourceline(*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 f72d98d4e146..1e6a4777fbd7 100644 --- a/src/calibre/gui2/tweak_book/live_css.py +++ b/src/calibre/gui2/tweak_book/live_css.py @@ -204,7 +204,7 @@ def mousePressEvent(self, ev): pos = ev.pos() if self.hyperlink_rect.contains(pos): self.emit_hyperlink_activated() - return QWidget.mouseMoveEvent(self, ev) + return QWidget.mousePressEvent(self, ev) def emit_hyperlink_activated(self): dt = self.data['type'] @@ -316,7 +316,7 @@ def __init__(self, preview, parent=None): s.addWidget(la) self.box = box = Box(self) - box.hyperlink_activated.connect(self.goto_declaration) + box.hyperlink_activated.connect(self.goto_declaration, type=Qt.QueuedConnection) self.scroll = sc = QScrollArea(self) sc.setWidget(box) sc.setWidgetResizable(True) @@ -409,5 +409,7 @@ def stop_update_timer(self): self.update_timer.stop() def navigate_to_declaration(self, data, editor): - print (data) # TODO: Implement this + if data['type'] == 'inline': + sourceline, tags = data['sourceline_address'] + editor.goto_sourceline(sourceline, tags, attribute='style')