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.editor.insert_resource import get_resource_data, NewBook
|
||||||
from calibre.gui2.tweak_book.preferences import Preferences
|
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.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 (
|
from calibre.gui2.tweak_book.widgets import (
|
||||||
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
|
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
|
||||||
InsertSemantics, BusyCursor, InsertTag)
|
InsertSemantics, BusyCursor, InsertTag)
|
||||||
@ -110,6 +111,7 @@ class Boss(QObject):
|
|||||||
self.gui.saved_searches.run_saved_searches.connect(self.run_saved_searches)
|
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.save_search.connect(self.save_search)
|
||||||
self.gui.central.search_panel.show_saved_searches.connect(self.show_saved_searches)
|
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):
|
def preferences(self):
|
||||||
p = Preferences(self.gui)
|
p = Preferences(self.gui)
|
||||||
@ -696,6 +698,16 @@ class Boss(QObject):
|
|||||||
run_search(state, action, ed, name, searchable_names,
|
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)
|
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):
|
def saved_searches(self):
|
||||||
self.gui.saved_searches.show(), self.gui.saved_searches.raise_()
|
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)
|
editor.modification_state_changed.connect(self.editor_modification_state_changed)
|
||||||
self.gui.central.add_editor(name, editor)
|
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)
|
editor = editors.get(name, None)
|
||||||
if editor is None:
|
if editor is None:
|
||||||
|
syntax = syntax or syntax_from_mime(name, guess_type(name))
|
||||||
if use_template is None:
|
if use_template is None:
|
||||||
data = current_container().raw_data(name)
|
data = current_container().raw_data(name)
|
||||||
if isbytestring(data) and syntax in {'html', 'css', 'text', 'xml'}:
|
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.syntax.css import CSSHighlighter
|
||||||
from calibre.gui2.tweak_book.editor.smart import NullSmarts
|
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.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'
|
PARAGRAPH_SEPARATOR = '\u2029'
|
||||||
entity_pat = re.compile(r'&(#{0,1}[a-zA-Z0-9]{1,8});')
|
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)
|
self.saved_matches[save_match] = (pat, m)
|
||||||
return True
|
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'):
|
def replace(self, pat, template, saved_match='gui'):
|
||||||
c = self.textCursor()
|
c = self.textCursor()
|
||||||
raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0')
|
raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0')
|
||||||
|
@ -189,6 +189,9 @@ class Editor(QMainWindow):
|
|||||||
def find(self, *args, **kwargs):
|
def find(self, *args, **kwargs):
|
||||||
return self.editor.find(*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):
|
def replace(self, *args, **kwargs):
|
||||||
return self.editor.replace(*args, **kwargs)
|
return self.editor.replace(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import cPickle, os, sys
|
import cPickle, os, sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict, OrderedDict
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from PyQt4.Qt import (
|
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.complete2 import LineEdit
|
||||||
from calibre.gui2.languages import LanguagesEdit
|
from calibre.gui2.languages import LanguagesEdit
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
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.gui2.tweak_book.widgets import Dialog
|
||||||
from calibre.spell.dictionary import (
|
from calibre.spell.dictionary import (
|
||||||
builtin_dictionaries, custom_dictionaries, best_locale_for_language,
|
builtin_dictionaries, custom_dictionaries, best_locale_for_language,
|
||||||
@ -670,6 +670,7 @@ class WordsModel(QAbstractTableModel):
|
|||||||
class SpellCheck(Dialog):
|
class SpellCheck(Dialog):
|
||||||
|
|
||||||
work_finished = pyqtSignal(object, object)
|
work_finished = pyqtSignal(object, object)
|
||||||
|
find_word = pyqtSignal(object, object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
self.__current_word = None
|
self.__current_word = None
|
||||||
@ -681,6 +682,7 @@ class SpellCheck(Dialog):
|
|||||||
self.setAttribute(Qt.WA_DeleteOnClose, False)
|
self.setAttribute(Qt.WA_DeleteOnClose, False)
|
||||||
|
|
||||||
def setup_ui(self):
|
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.setWindowIcon(QIcon(I('spell-check.png')))
|
||||||
self.l = l = QVBoxLayout(self)
|
self.l = l = QVBoxLayout(self)
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
@ -720,6 +722,8 @@ class SpellCheck(Dialog):
|
|||||||
m.h2 = h = QHBoxLayout()
|
m.h2 = h = QHBoxLayout()
|
||||||
l.addLayout(h)
|
l.addLayout(h)
|
||||||
self.words_view = w = QTableView(m)
|
self.words_view = w = QTableView(m)
|
||||||
|
set_no_activate_on_click(w)
|
||||||
|
w.activated.connect(self.word_activated)
|
||||||
w.currentChanged = self.current_word_changed
|
w.currentChanged = self.current_word_changed
|
||||||
state = tprefs.get('spell-check-table-state', None)
|
state = tprefs.get('spell-check-table-state', None)
|
||||||
hh = self.words_view.horizontalHeader()
|
hh = self.words_view.horizontalHeader()
|
||||||
@ -760,6 +764,11 @@ class SpellCheck(Dialog):
|
|||||||
self.initialize_user_dictionaries()
|
self.initialize_user_dictionaries()
|
||||||
d.setMinimumContentsLength(25)
|
d.setMinimumContentsLength(25)
|
||||||
l.addWidget(b), l.addWidget(d), l.addWidget(la)
|
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)
|
l.addStretch(1)
|
||||||
|
|
||||||
self.change_button = b = QPushButton(_('&Change selected word to:'), self)
|
self.change_button = b = QPushButton(_('&Change selected word to:'), self)
|
||||||
@ -772,8 +781,7 @@ class SpellCheck(Dialog):
|
|||||||
self.suggested_list = sl = QListWidget(self)
|
self.suggested_list = sl = QListWidget(self)
|
||||||
sl.currentItemChanged.connect(self.current_suggestion_changed)
|
sl.currentItemChanged.connect(self.current_suggestion_changed)
|
||||||
sl.itemActivated.connect(self.change_word)
|
sl.itemActivated.connect(self.change_word)
|
||||||
pi = plugins['progress_indicator'][0]
|
set_no_activate_on_click(sl)
|
||||||
pi.set_no_activate_on_click(sl)
|
|
||||||
l.addWidget(sl)
|
l.addWidget(sl)
|
||||||
|
|
||||||
hh.setSectionHidden(3, m.show_only_misspelt)
|
hh.setSectionHidden(3, m.show_only_misspelt)
|
||||||
@ -784,6 +792,15 @@ class SpellCheck(Dialog):
|
|||||||
self.summary = s = QLabel('')
|
self.summary = s = QLabel('')
|
||||||
self.main.l.addLayout(h), h.addWidget(s), h.addWidget(om), h.addStretch(10)
|
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):
|
def initialize_user_dictionaries(self):
|
||||||
ct = unicode(self.user_dictionaries.currentText())
|
ct = unicode(self.user_dictionaries.currentText())
|
||||||
self.user_dictionaries.clear()
|
self.user_dictionaries.clear()
|
||||||
@ -961,6 +978,42 @@ class SpellCheck(Dialog):
|
|||||||
d.exec_()
|
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__':
|
if __name__ == '__main__':
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
dictionaries.initialize()
|
dictionaries.initialize()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user