mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add controls to show next occurrence of selected word
This commit is contained in:
parent
e591101202
commit
303d5f04fe
@ -38,6 +38,7 @@ from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime
|
||||
from calibre.gui2.tweak_book.editor.insert_resource import get_resource_data, NewBook
|
||||
from calibre.gui2.tweak_book.preferences import Preferences
|
||||
from calibre.gui2.tweak_book.search import validate_search_request, run_search
|
||||
from calibre.gui2.tweak_book.spell import find_next as find_next_word
|
||||
from calibre.gui2.tweak_book.widgets import (
|
||||
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
|
||||
InsertSemantics, BusyCursor, InsertTag)
|
||||
@ -110,6 +111,7 @@ class Boss(QObject):
|
||||
self.gui.saved_searches.run_saved_searches.connect(self.run_saved_searches)
|
||||
self.gui.central.search_panel.save_search.connect(self.save_search)
|
||||
self.gui.central.search_panel.show_saved_searches.connect(self.show_saved_searches)
|
||||
self.gui.spell_check.find_word.connect(self.find_word)
|
||||
|
||||
def preferences(self):
|
||||
p = Preferences(self.gui)
|
||||
@ -696,6 +698,16 @@ class Boss(QObject):
|
||||
run_search(state, action, ed, name, searchable_names,
|
||||
self.gui, self.show_editor, self.edit_file, self.show_current_diff, self.add_savepoint, self.rewind_savepoint, self.set_modified)
|
||||
|
||||
def find_word(self, word, locations):
|
||||
' Go to a word from the spell check dialog '
|
||||
ed = self.gui.central.current_editor
|
||||
name = None
|
||||
for n, x in editors.iteritems():
|
||||
if x is ed:
|
||||
name = n
|
||||
break
|
||||
find_next_word(word, locations, ed, name, self.gui, self.show_editor, self.edit_file)
|
||||
|
||||
def saved_searches(self):
|
||||
self.gui.saved_searches.show(), self.gui.saved_searches.raise_()
|
||||
|
||||
@ -1002,9 +1014,10 @@ class Boss(QObject):
|
||||
editor.modification_state_changed.connect(self.editor_modification_state_changed)
|
||||
self.gui.central.add_editor(name, editor)
|
||||
|
||||
def edit_file(self, name, syntax, use_template=None):
|
||||
def edit_file(self, name, syntax=None, use_template=None):
|
||||
editor = editors.get(name, None)
|
||||
if editor is None:
|
||||
syntax = syntax or syntax_from_mime(name, guess_type(name))
|
||||
if use_template is None:
|
||||
data = current_container().raw_data(name)
|
||||
if isbytestring(data) and syntax in {'html', 'css', 'text', 'xml'}:
|
||||
|
@ -25,7 +25,8 @@ 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.utils.icu import safe_chr
|
||||
from calibre.spell.break_iterator import index_of
|
||||
from calibre.utils.icu import safe_chr, string_length
|
||||
|
||||
PARAGRAPH_SEPARATOR = '\u2029'
|
||||
entity_pat = re.compile(r'&(#{0,1}[a-zA-Z0-9]{1,8});')
|
||||
@ -377,6 +378,29 @@ class TextEdit(PlainTextEdit):
|
||||
self.saved_matches[save_match] = (pat, m)
|
||||
return True
|
||||
|
||||
def find_word_in_line(self, word, lang, lnum, from_cursor=True):
|
||||
c = self.textCursor()
|
||||
c.setPosition(c.position())
|
||||
if not from_cursor or c.blockNumber() != lnum - 1:
|
||||
lnum = max(1, min(self.blockCount(), lnum))
|
||||
c.movePosition(c.Start)
|
||||
c.movePosition(c.NextBlock, n=lnum - 1)
|
||||
c.movePosition(c.StartOfLine)
|
||||
c.movePosition(c.EndOfBlock, c.KeepAnchor)
|
||||
offset = c.block().position()
|
||||
else:
|
||||
offset = c.block().position() + c.positionInBlock()
|
||||
c.movePosition(c.EndOfBlock, c.KeepAnchor)
|
||||
text = unicode(c.selectedText()).rstrip('\0')
|
||||
idx = index_of(word, text, lang=lang)
|
||||
if idx == -1:
|
||||
return False
|
||||
c.setPosition(offset + idx)
|
||||
c.setPosition(c.position() + string_length(word), c.KeepAnchor)
|
||||
self.setTextCursor(c)
|
||||
self.centerCursor()
|
||||
return True
|
||||
|
||||
def replace(self, pat, template, saved_match='gui'):
|
||||
c = self.textCursor()
|
||||
raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0')
|
||||
|
@ -189,6 +189,9 @@ class Editor(QMainWindow):
|
||||
def find(self, *args, **kwargs):
|
||||
return self.editor.find(*args, **kwargs)
|
||||
|
||||
def find_word_in_line(self, *args, **kwargs):
|
||||
return self.editor.find_word_in_line(*args, **kwargs)
|
||||
|
||||
def replace(self, *args, **kwargs):
|
||||
return self.editor.replace(*args, **kwargs)
|
||||
|
||||
|
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import cPickle, os, sys
|
||||
from collections import defaultdict
|
||||
from collections import defaultdict, OrderedDict
|
||||
from threading import Thread
|
||||
|
||||
from PyQt4.Qt import (
|
||||
@ -22,7 +22,7 @@ from calibre.gui2 import choose_files, error_dialog
|
||||
from calibre.gui2.complete2 import LineEdit
|
||||
from calibre.gui2.languages import LanguagesEdit
|
||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||
from calibre.gui2.tweak_book import dictionaries, current_container, set_book_locale, tprefs
|
||||
from calibre.gui2.tweak_book import dictionaries, current_container, set_book_locale, tprefs, editors
|
||||
from calibre.gui2.tweak_book.widgets import Dialog
|
||||
from calibre.spell.dictionary import (
|
||||
builtin_dictionaries, custom_dictionaries, best_locale_for_language,
|
||||
@ -670,6 +670,7 @@ class WordsModel(QAbstractTableModel):
|
||||
class SpellCheck(Dialog):
|
||||
|
||||
work_finished = pyqtSignal(object, object)
|
||||
find_word = pyqtSignal(object, object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
self.__current_word = None
|
||||
@ -681,6 +682,7 @@ class SpellCheck(Dialog):
|
||||
self.setAttribute(Qt.WA_DeleteOnClose, False)
|
||||
|
||||
def setup_ui(self):
|
||||
set_no_activate_on_click = plugins['progress_indicator'][0].set_no_activate_on_click
|
||||
self.setWindowIcon(QIcon(I('spell-check.png')))
|
||||
self.l = l = QVBoxLayout(self)
|
||||
self.setLayout(l)
|
||||
@ -720,6 +722,8 @@ class SpellCheck(Dialog):
|
||||
m.h2 = h = QHBoxLayout()
|
||||
l.addLayout(h)
|
||||
self.words_view = w = QTableView(m)
|
||||
set_no_activate_on_click(w)
|
||||
w.activated.connect(self.word_activated)
|
||||
w.currentChanged = self.current_word_changed
|
||||
state = tprefs.get('spell-check-table-state', None)
|
||||
hh = self.words_view.horizontalHeader()
|
||||
@ -760,6 +764,11 @@ class SpellCheck(Dialog):
|
||||
self.initialize_user_dictionaries()
|
||||
d.setMinimumContentsLength(25)
|
||||
l.addWidget(b), l.addWidget(d), l.addWidget(la)
|
||||
self.next_occurrence = b = QPushButton(_('Show &next occurrence'), self)
|
||||
b.setToolTip('<p>' + _(
|
||||
'Show the next occurrence of the selected word in the editor, so you can edit it manually'))
|
||||
b.clicked.connect(self.show_next_occurrence)
|
||||
l.addSpacing(20), l.addWidget(b)
|
||||
l.addStretch(1)
|
||||
|
||||
self.change_button = b = QPushButton(_('&Change selected word to:'), self)
|
||||
@ -772,8 +781,7 @@ class SpellCheck(Dialog):
|
||||
self.suggested_list = sl = QListWidget(self)
|
||||
sl.currentItemChanged.connect(self.current_suggestion_changed)
|
||||
sl.itemActivated.connect(self.change_word)
|
||||
pi = plugins['progress_indicator'][0]
|
||||
pi.set_no_activate_on_click(sl)
|
||||
set_no_activate_on_click(sl)
|
||||
l.addWidget(sl)
|
||||
|
||||
hh.setSectionHidden(3, m.show_only_misspelt)
|
||||
@ -784,6 +792,15 @@ class SpellCheck(Dialog):
|
||||
self.summary = s = QLabel('')
|
||||
self.main.l.addLayout(h), h.addWidget(s), h.addWidget(om), h.addStretch(10)
|
||||
|
||||
def show_next_occurrence(self):
|
||||
self.word_activated(self.words_view.currentIndex())
|
||||
|
||||
def word_activated(self, index):
|
||||
w = self.words_model.word_for_row(index.row())
|
||||
if w is None:
|
||||
return
|
||||
self.find_word.emit(w, self.words_model.words[w])
|
||||
|
||||
def initialize_user_dictionaries(self):
|
||||
ct = unicode(self.user_dictionaries.currentText())
|
||||
self.user_dictionaries.clear()
|
||||
@ -961,6 +978,42 @@ class SpellCheck(Dialog):
|
||||
d.exec_()
|
||||
# }}}
|
||||
|
||||
def find_next(word, locations, current_editor, current_editor_name,
|
||||
gui_parent, show_editor, edit_file):
|
||||
files = OrderedDict()
|
||||
for l in locations:
|
||||
try:
|
||||
files[l.file_name].append(l)
|
||||
except KeyError:
|
||||
files[l.file_name] = [l]
|
||||
start_locations = set()
|
||||
|
||||
if current_editor_name not in files:
|
||||
current_editor = current_editor_name = None
|
||||
else:
|
||||
# Re-order the list of locations to search so that we search int he
|
||||
# current editor first
|
||||
lfiles = list(files)
|
||||
idx = lfiles.index(current_editor_name)
|
||||
before, after = lfiles[:idx], lfiles[idx+1:]
|
||||
lfiles = after + before + [current_editor_name]
|
||||
lnum = current_editor.current_line
|
||||
start_locations = [l for l in files[current_editor_name] if l.sourceline >= lnum]
|
||||
locations = list(start_locations)
|
||||
for fname in lfiles:
|
||||
locations.extend(files[fname])
|
||||
start_locations = set(start_locations)
|
||||
|
||||
for location in locations:
|
||||
ed = editors.get(location.file_name, None)
|
||||
if ed is None:
|
||||
edit_file(location.file_name)
|
||||
ed = editors[location.file_name]
|
||||
if ed.find_word_in_line(location.original_word, word[1].langcode, location.sourceline, from_cursor=location in start_locations):
|
||||
show_editor(location.file_name)
|
||||
return True
|
||||
return False
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
dictionaries.initialize()
|
||||
|
Loading…
x
Reference in New Issue
Block a user