Search/replace now fully implemented, although only for currently edited document

This commit is contained in:
Veronica K. B. Olsen
2019-10-06 15:36:34 +02:00
parent 3b34408d33
commit d91d77d5ab
5 changed files with 133 additions and 35 deletions
+4 -2
View File
@@ -66,8 +66,10 @@ class nwDocAction(Enum):
SEL_ALL = 11 SEL_ALL = 11
SEL_PARA = 12 SEL_PARA = 12
FIND = 13 FIND = 13
GO_NEXT = 14 REPLACE = 14
GO_PREV = 15 GO_NEXT = 15
GO_PREV = 16
REPL_NEXT = 17
# END Enum nwDocAction # END Enum nwDocAction
+48 -21
View File
@@ -267,21 +267,23 @@ class GuiDocEditor(QTextEdit):
if not self.theParent.hasProject: if not self.theParent.hasProject:
logger.error("No project open") logger.error("No project open")
return False return False
if theAction == nwDocAction.UNDO: self.undo() if theAction == nwDocAction.UNDO: self.undo()
elif theAction == nwDocAction.REDO: self.redo() elif theAction == nwDocAction.REDO: self.redo()
elif theAction == nwDocAction.CUT: self.cut() elif theAction == nwDocAction.CUT: self.cut()
elif theAction == nwDocAction.COPY: self.copy() elif theAction == nwDocAction.COPY: self.copy()
elif theAction == nwDocAction.PASTE: self.paste() elif theAction == nwDocAction.PASTE: self.paste()
elif theAction == nwDocAction.BOLD: self._wrapSelection("**","**") elif theAction == nwDocAction.BOLD: self._wrapSelection("**","**")
elif theAction == nwDocAction.ITALIC: self._wrapSelection("_","_") elif theAction == nwDocAction.ITALIC: self._wrapSelection("_","_")
elif theAction == nwDocAction.U_LINE: self._wrapSelection("__","__") elif theAction == nwDocAction.U_LINE: self._wrapSelection("__","__")
elif theAction == nwDocAction.S_QUOTE: self._wrapSelection(self.typSQOpen,self.typSQClose) elif theAction == nwDocAction.S_QUOTE: self._wrapSelection(self.typSQOpen,self.typSQClose)
elif theAction == nwDocAction.D_QUOTE: self._wrapSelection(self.typDQOpen,self.typDQClose) elif theAction == nwDocAction.D_QUOTE: self._wrapSelection(self.typDQOpen,self.typDQClose)
elif theAction == nwDocAction.SEL_ALL: self._makeSelection(QTextCursor.Document) elif theAction == nwDocAction.SEL_ALL: self._makeSelection(QTextCursor.Document)
elif theAction == nwDocAction.SEL_PARA: self._makeSelection(QTextCursor.BlockUnderCursor) elif theAction == nwDocAction.SEL_PARA: self._makeSelection(QTextCursor.BlockUnderCursor)
elif theAction == nwDocAction.FIND: self._beginSearch() elif theAction == nwDocAction.FIND: self._beginSearch()
elif theAction == nwDocAction.GO_NEXT: self._findNext() elif theAction == nwDocAction.REPLACE: self._beginReplace()
elif theAction == nwDocAction.GO_PREV: self._findPrev() elif theAction == nwDocAction.GO_NEXT: self._findNext()
elif theAction == nwDocAction.GO_PREV: self._findPrev()
elif theAction == nwDocAction.REPL_NEXT: self._replaceNext()
else: else:
logger.error("Unknown or unsupported document action %s" % str(theAction)) logger.error("Unknown or unsupported document action %s" % str(theAction))
return False return False
@@ -489,21 +491,26 @@ class GuiDocEditor(QTextEdit):
return return
def _beginSearch(self): def _beginSearch(self):
"""Sets the selected text as the search text for the search bar.
"""
theCursor = self.textCursor() theCursor = self.textCursor()
if theCursor.hasSelection(): if theCursor.hasSelection():
selText = theCursor.selectedText() selText = theCursor.selectedText()
else: else:
selText = "" selText = ""
self.theParent.searchBar.setSearchText(selText) self.theParent.searchBar.setSearchText(selText)
return
if selText != "": def _beginReplace(self):
self._findNext() """Opens the replace line of the search bar and sets the replace text.
"""
self.theParent.searchBar.setReplaceText("")
return return
def _findNext(self): def _findNext(self):
"""Searches for the next occurrence of the search bar text in the document.
Wraps back to the top if not found.
"""
searchFor = self.theParent.searchBar.getSearchText() searchFor = self.theParent.searchBar.getSearchText()
wasFound = self.find(searchFor) wasFound = self.find(searchFor)
if not wasFound: if not wasFound:
@@ -513,12 +520,32 @@ class GuiDocEditor(QTextEdit):
return return
def _findPrev(self): def _findPrev(self):
"""Searches for the previous occurrence of the search bar text in the document.
Wraps back to the end if not found.
"""
searchFor = self.theParent.searchBar.getSearchText() searchFor = self.theParent.searchBar.getSearchText()
wasFound = self.find(searchFor, QTextDocument.FindBackward) wasFound = self.find(searchFor, QTextDocument.FindBackward)
if not wasFound: if not wasFound:
theCursor = self.textCursor() theCursor = self.textCursor()
theCursor.movePosition(QTextCursor.End) theCursor.movePosition(QTextCursor.End)
self.setTextCursor(theCursor) self.setTextCursor(theCursor)
return return
def _replaceNext(self):
"""Searches for the next occurrence of the search bar text in the document and replaces it
with the replace text. Wraps back to the top if not found.
"""
theCursor = self.textCursor()
replWith = self.theParent.searchBar.getReplaceText()
if theCursor.hasSelection():
xPos = theCursor.selectionStart()
theCursor.beginEditBlock()
theCursor.removeSelectedText()
theCursor.insertText(replWith)
theCursor.endEditBlock()
theCursor.setPosition(xPos)
self.setTextCursor(theCursor)
self._findNext()
return
# END Class GuiDocEditor # END Class GuiDocEditor
+14
View File
@@ -405,6 +405,13 @@ class GuiMainMenu(QMenuBar):
menuItem.triggered.connect(lambda: self._docAction(nwDocAction.FIND)) menuItem.triggered.connect(lambda: self._docAction(nwDocAction.FIND))
self.editMenu.addAction(menuItem) self.editMenu.addAction(menuItem)
# Edit > Replace
menuItem = QAction(QIcon.fromTheme("edit-find-replace"), "Replace", self)
menuItem.setStatusTip("Replace text in document")
menuItem.setShortcut("Ctrl+H")
menuItem.triggered.connect(lambda: self._docAction(nwDocAction.REPLACE))
self.editMenu.addAction(menuItem)
# Edit > Find Next # Edit > Find Next
menuItem = QAction(QIcon.fromTheme("go-next"), "Go Next", self) menuItem = QAction(QIcon.fromTheme("go-next"), "Go Next", self)
menuItem.setStatusTip("Find next occurrence text in document") menuItem.setStatusTip("Find next occurrence text in document")
@@ -419,6 +426,13 @@ class GuiMainMenu(QMenuBar):
menuItem.triggered.connect(lambda: self._docAction(nwDocAction.GO_PREV)) menuItem.triggered.connect(lambda: self._docAction(nwDocAction.GO_PREV))
self.editMenu.addAction(menuItem) self.editMenu.addAction(menuItem)
# Edit > Replace Next
menuItem = QAction(QIcon.fromTheme("go-next"), "Replace Next", self)
menuItem.setStatusTip("Find and replace next occurrence text in document")
menuItem.setShortcut("Ctrl+Shift+1")
menuItem.triggered.connect(lambda: self._docAction(nwDocAction.REPL_NEXT))
self.editMenu.addAction(menuItem)
# Edit > Separator # Edit > Separator
self.editMenu.addSeparator() self.editMenu.addSeparator()
+58 -12
View File
@@ -13,8 +13,9 @@
import logging import logging
import nw import nw
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QFrame, QGridLayout, QLabel, QLineEdit, QPushButton from PyQt5.QtWidgets import QFrame, QGridLayout, QLabel, QLineEdit, QPushButton, QApplication
from nw.enum import nwDocAction from nw.enum import nwDocAction
@@ -27,26 +28,37 @@ class GuiSearchBar(QFrame):
logger.debug("Initialising GuiSearchBar ...") logger.debug("Initialising GuiSearchBar ...")
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.theParent = theParent self.theParent = theParent
self.repVisible = False
self.setContentsMargins(0,0,0,0) self.setContentsMargins(0,0,0,0)
self.mainBox = QGridLayout(self) self.mainBox = QGridLayout(self)
self.setLayout(self.mainBox) self.setLayout(self.mainBox)
self.searchBox = QLineEdit() self.searchBox = QLineEdit()
self.closeButton = QPushButton(QIcon.fromTheme("edit-delete"),"") self.replaceBox = QLineEdit()
self.searchButton = QPushButton(QIcon.fromTheme("edit-find"),"") self.searchLabel = QLabel("Search")
self.replaceLabel = QLabel("Replace")
self.closeButton = QPushButton(QIcon.fromTheme("edit-delete"),"")
self.searchButton = QPushButton(QIcon.fromTheme("edit-find"),"")
self.replaceButton = QPushButton(QIcon.fromTheme("edit-find-replace"),"")
self.closeButton.clicked.connect(self._doClose) self.closeButton.clicked.connect(self._doClose)
self.searchButton.clicked.connect(self._doSearch) self.searchButton.clicked.connect(self._doSearch)
self.replaceButton.clicked.connect(self._doReplace)
self.searchBox.returnPressed.connect(self._doSearch)
self.replaceBox.returnPressed.connect(self._doSearch)
self.mainBox.addWidget(QLabel(""), 0,0) self.mainBox.addWidget(QLabel(""), 0,0)
self.mainBox.addWidget(QLabel("Search"), 0,1) self.mainBox.addWidget(self.searchLabel, 0,1)
self.mainBox.addWidget(self.searchBox, 0,2) self.mainBox.addWidget(self.searchBox, 0,2)
self.mainBox.addWidget(self.searchButton,0,3) self.mainBox.addWidget(self.searchButton, 0,3)
self.mainBox.addWidget(self.closeButton, 0,4) self.mainBox.addWidget(self.closeButton, 0,4)
self.mainBox.addWidget(self.replaceLabel, 1,1)
self.mainBox.addWidget(self.replaceBox, 1,2)
self.mainBox.addWidget(self.replaceButton, 1,3)
self.mainBox.setColumnStretch(0,1) self.mainBox.setColumnStretch(0,1)
self.mainBox.setColumnStretch(1,0) self.mainBox.setColumnStretch(1,0)
@@ -55,10 +67,19 @@ class GuiSearchBar(QFrame):
self.mainBox.setColumnStretch(4,0) self.mainBox.setColumnStretch(4,0)
self.mainBox.setContentsMargins(0,0,0,0) self.mainBox.setContentsMargins(0,0,0,0)
self.searchBox.setMinimumWidth(180)
self.replaceBox.setMinimumWidth(180)
self._replaceVisible(False)
logger.debug("GuiSearchBar initialisation complete") logger.debug("GuiSearchBar initialisation complete")
return return
##
# Get and Set Functions
##
def setSearchText(self, theText): def setSearchText(self, theText):
if not self.isVisible(): if not self.isVisible():
@@ -71,19 +92,44 @@ class GuiSearchBar(QFrame):
return True return True
def setReplaceText(self, theText):
self._replaceVisible(True)
self.replaceBox.setFocus(True)
self.replaceBox.setText(theText)
return True
def getSearchText(self): def getSearchText(self):
return self.searchBox.text() return self.searchBox.text()
def getReplaceText(self):
return self.replaceBox.text()
## ##
# Internal Functions # Internal Functions
## ##
def _doClose(self): def _doClose(self):
self._replaceVisible(False)
self.setVisible(False) self.setVisible(False)
return return
def _doSearch(self): def _doSearch(self):
self.theParent.docEditor.docAction(nwDocAction.GO_NEXT) modKey = QApplication.keyboardModifiers()
if modKey == Qt.ShiftModifier:
self.theParent.docEditor.docAction(nwDocAction.GO_PREV)
else:
self.theParent.docEditor.docAction(nwDocAction.GO_NEXT)
return return
def _doReplace(self):
self.theParent.docEditor.docAction(nwDocAction.REPL_NEXT)
return
def _replaceVisible(self, isVisible):
self.replaceLabel.setVisible(isVisible)
self.replaceBox.setVisible(isVisible)
self.replaceButton.setVisible(isVisible)
self.repVisible = isVisible
return True
# END Class GuiSearchBar # END Class GuiSearchBar
+9
View File
@@ -137,6 +137,7 @@ class GuiMain(QMainWindow):
# Keyboard Shortcuts # Keyboard Shortcuts
QShortcut(Qt.Key_Return, self.treeView, context=Qt.WidgetShortcut, activated=self._treeKeyPressReturn) QShortcut(Qt.Key_Return, self.treeView, context=Qt.WidgetShortcut, activated=self._treeKeyPressReturn)
QShortcut(Qt.Key_Escape, self, activated=self._keyPressEscape)
# Forward Functions # Forward Functions
self.setStatus = self.statusBar.setStatus self.setStatus = self.statusBar.setStatus
@@ -689,6 +690,14 @@ class GuiMain(QMainWindow):
logger.verbose("Requested item %s is a folder" % tHandle) logger.verbose("Requested item %s is a folder" % tHandle)
return return
def _keyPressEscape(self):
"""When the escape key is pressed somewhere in the main window, do the following, in order
"""
if self.searchBar.isVisible():
self.searchBar.setVisible(False)
return
return
def _splitMainMove(self, pWidth, pHeight): def _splitMainMove(self, pWidth, pHeight):
"""Alert dependent GUI elements that the main pane splitter has been moved. """Alert dependent GUI elements that the main pane splitter has been moved.
""" """