diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index de605790fb..c5fbfeccf5 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -643,9 +643,9 @@ class Boss(QObject): ed.insert_image(href) elif action[0] == 'insert_hyperlink': self.commit_all_editors_to_container() - d = InsertLink(current_container(), edname, parent=self.gui) + d = InsertLink(current_container(), edname, initial_text=ed.get_smart_selection(), parent=self.gui) if d.exec_() == d.Accepted: - ed.insert_hyperlink(d.href) + ed.insert_hyperlink(d.href, d.text) else: ed.action_triggered(action) diff --git a/src/calibre/gui2/tweak_book/editor/smart/__init__.py b/src/calibre/gui2/tweak_book/editor/smart/__init__.py index 7cb00ca997..b13c22032a 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/__init__.py +++ b/src/calibre/gui2/tweak_book/editor/smart/__init__.py @@ -14,3 +14,6 @@ class NullSmarts(object): def get_extra_selections(self, editor): return () + def get_smart_selection(self, editor, update=True): + return editor.selected_text + diff --git a/src/calibre/gui2/tweak_book/editor/smart/html.py b/src/calibre/gui2/tweak_book/editor/smart/html.py index bd4a5b568f..a0b2a1ba77 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/html.py +++ b/src/calibre/gui2/tweak_book/editor/smart/html.py @@ -129,7 +129,7 @@ def rename_tag(cursor, opening_tag, closing_tag, new_name, insert=False): cursor.insertText(text) cursor.endEditBlock() -def ensure_not_within_tag_definition(cursor): +def ensure_not_within_tag_definition(cursor, forward=True): ''' Ensure the cursor is not inside a tag definition <>. Returns True iff the cursor was moved. ''' block, offset = cursor.block(), cursor.positionInBlock() b, boundary = next_tag_boundary(block, offset, forward=False) @@ -137,10 +137,15 @@ def ensure_not_within_tag_definition(cursor): return False if boundary.is_start: # We are inside a tag - block, boundary = next_tag_boundary(block, offset) - if block is not None: - cursor.setPosition(block.position() + boundary.offset + 1) + if forward: + block, boundary = next_tag_boundary(block, offset) + if block is not None: + cursor.setPosition(block.position() + boundary.offset + 1) + return True + else: + cursor.setPosition(b.position() + boundary.offset) return True + return False class HTMLSmarts(NullSmarts): @@ -195,7 +200,27 @@ class HTMLSmarts(NullSmarts): return error_dialog(editor, _('No found'), _( 'No suitable block level tag was found to rename'), show=True) - def insert_hyperlink(self, editor, target): + def get_smart_selection(self, editor, update=True): + cursor = editor.textCursor() + if not cursor.hasSelection(): + return '' + left = min(cursor.anchor(), cursor.position()) + right = max(cursor.anchor(), cursor.position()) + + cursor.setPosition(left) + ensure_not_within_tag_definition(cursor) + left = cursor.position() + + cursor.setPosition(right) + ensure_not_within_tag_definition(cursor, forward=False) + right = cursor.position() + + cursor.setPosition(left), cursor.setPosition(right, cursor.KeepAnchor) + if update: + editor.setTextCursor(cursor) + return editor.selected_text_from_cursor(cursor) + + def insert_hyperlink(self, editor, target, text): c = editor.textCursor() if c.hasSelection(): c.insertText('') # delete any existing selected text @@ -204,4 +229,6 @@ class HTMLSmarts(NullSmarts): p = c.position() c.insertText('') c.setPosition(p) # ensure cursor is positioned inside the newly created tag + if text: + c.insertText(text) editor.setTextCursor(c) diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index cccb274c97..d310906ea4 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -101,9 +101,12 @@ class PlainTextEdit(QPlainTextEdit): self.copy() self.textCursor().removeSelectedText() + def selected_text_from_cursor(self, cursor): + return unicodedata.normalize('NFC', unicode(cursor.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0')) + @property def selected_text(self): - return unicodedata.normalize('NFC', unicode(self.textCursor().selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0')) + return self.selected_text_from_cursor(self.textCursor()) def selection_changed(self): # Workaround Qt replacing nbsp with normal spaces on copy @@ -602,9 +605,9 @@ class TextEdit(PlainTextEdit): c.setPosition(left + len(text), c.KeepAnchor) self.setTextCursor(c) - def insert_hyperlink(self, target): + def insert_hyperlink(self, target, text): if hasattr(self.smarts, 'insert_hyperlink'): - self.smarts.insert_hyperlink(self, target) + self.smarts.insert_hyperlink(self, target, text) def keyPressEvent(self, ev): if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier: diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index d4c7de956a..81a54d17a9 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -144,8 +144,8 @@ class Editor(QMainWindow): def insert_image(self, href): self.editor.insert_image(href) - def insert_hyperlink(self, href): - self.editor.insert_hyperlink(href) + def insert_hyperlink(self, href, text): + self.editor.insert_hyperlink(href, text) def undo(self): self.editor.undo() @@ -157,6 +157,9 @@ class Editor(QMainWindow): def selected_text(self): return self.editor.selected_text + def get_smart_selection(self, update=True): + return self.editor.smarts.get_smart_selection(self.editor, update=update) + # Search and replace {{{ def mark_selected_text(self): self.editor.mark_selected_text() diff --git a/src/calibre/gui2/tweak_book/widgets.py b/src/calibre/gui2/tweak_book/widgets.py index 445eaf493a..0ca22d1832 100644 --- a/src/calibre/gui2/tweak_book/widgets.py +++ b/src/calibre/gui2/tweak_book/widgets.py @@ -537,9 +537,10 @@ def create_filterable_names_list(names, filter_text=None, parent=None): # Insert Link {{{ class InsertLink(Dialog): - def __init__(self, container, source_name, parent=None): + def __init__(self, container, source_name, initial_text=None, parent=None): self.container = container self.source_name = source_name + self.initial_text = initial_text Dialog.__init__(self, _('Insert Hyperlink'), 'insert-hyperlink', parent=parent) self.anchor_cache = {} @@ -573,14 +574,18 @@ class InsertLink(Dialog): fnl.addWidget(la), fnl.addWidget(f), fnl.addWidget(fn) h.addLayout(fnl), h.setStretch(1, 1) - self.tl = tl = QHBoxLayout() - self.la3 = la = QLabel(_('&Target:')) - tl.addWidget(la) + self.tl = tl = QFormLayout() self.target = t = QLineEdit(self) - la.setBuddy(t) - tl.addWidget(t) + t.setPlaceholderText(_('The destination (href) for the link')) + tl.addRow(_('&Target:'), t) l.addLayout(tl) + self.text_edit = t = QLineEdit(self) + la.setBuddy(t) + tl.addRow(_('Te&xt:'), t) + t.setText(self.initial_text or '') + t.setPlaceholderText(_('The (optional) text for the link')) + l.addWidget(self.bb) def selected_file_changed(self, *args): @@ -622,6 +627,10 @@ class InsertLink(Dialog): def href(self): return unicode(self.target.text()).strip() + @property + def text(self): + return unicode(self.text_edit.text()).strip() + @classmethod def test(cls): import sys @@ -629,7 +638,7 @@ class InsertLink(Dialog): c = get_container(sys.argv[-1], tweak_mode=True) d = cls(c, next(c.spine_names)[0]) if d.exec_() == d.Accepted: - print (d.href) + print (d.href, d.text) # }}}