From f6c92dab243087e7912a9ab5b8dc117037275a8d Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 17 Mar 2021 16:43:45 +0100 Subject: [PATCH 1/6] Make the editor search/replace bypass the docAction pipeline and be called directly --- nw/constants/enum.py | 21 ++- nw/gui/doceditor.py | 295 ++++++++++++++++++++++--------------------- nw/gui/mainmenu.py | 10 +- nw/guimain.py | 12 +- 4 files changed, 172 insertions(+), 166 deletions(-) diff --git a/nw/constants/enum.py b/nw/constants/enum.py index 539c0ae0..4e800666 100644 --- a/nw/constants/enum.py +++ b/nw/constants/enum.py @@ -81,19 +81,14 @@ class nwDocAction(Enum): D_QUOTE = 10 SEL_ALL = 11 SEL_PARA = 12 - FIND = 13 - REPLACE = 14 - GO_NEXT = 15 - GO_PREV = 16 - REPL_NEXT = 17 - BLOCK_H1 = 18 - BLOCK_H2 = 19 - BLOCK_H3 = 20 - BLOCK_H4 = 21 - BLOCK_COM = 22 - BLOCK_TXT = 23 - REPL_SNG = 24 - REPL_DBL = 25 + BLOCK_H1 = 13 + BLOCK_H2 = 14 + BLOCK_H3 = 15 + BLOCK_H4 = 16 + BLOCK_COM = 17 + BLOCK_TXT = 18 + REPL_SNG = 19 + REPL_DBL = 20 # END Enum nwDocAction diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index abafc6fb..779d475d 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -660,10 +660,6 @@ class GuiDocEditor(QTextEdit): this class when calling these actions from other classes. """ logger.verbose("Requesting action: %s" % theAction.name) - if not self.hasFocus(): - logger.verbose("Editor does not have focus") - return False - if self.theHandle is None: logger.error("No document open") return False @@ -693,16 +689,6 @@ class GuiDocEditor(QTextEdit): self._makeSelection(QTextCursor.Document) elif theAction == nwDocAction.SEL_PARA: self._makeSelection(QTextCursor.BlockUnderCursor) - elif theAction == nwDocAction.FIND: - self._beginSearch() - elif theAction == nwDocAction.REPLACE: - self._beginReplace() - elif theAction == nwDocAction.GO_NEXT: - self._findNext() - elif theAction == nwDocAction.GO_PREV: - self._findNext(isBackward=True) - elif theAction == nwDocAction.REPL_NEXT: - self._replaceNext() elif theAction == nwDocAction.BLOCK_H1: self._formatBlock(nwDocAction.BLOCK_H1) elif theAction == nwDocAction.BLOCK_H2: @@ -734,6 +720,15 @@ class GuiDocEditor(QTextEdit): """ return self.qDocument.isEmpty() + def anyFocus(self): + """Check if any widget or child widget has focus. + """ + if self.hasFocus(): + return True + if self.isAncestorOf(qApp.focusWidget()): + return True + return False + def revealLocation(self): """Tell the user where on the file system the file in the editor is saved. @@ -827,7 +822,7 @@ class GuiDocEditor(QTextEdit): if self.docSearch.isVisible(): self.docSearch.closeSearch() else: - self._beginSearch() + self.beginSearch() return ## @@ -1142,6 +1137,144 @@ class GuiDocEditor(QTextEdit): ) return + ## + # Search & Replace + ## + + def beginSearch(self): + """Sets the selected text as the search text for the search bar. + """ + theCursor = self.textCursor() + if theCursor.hasSelection(): + self.docSearch.setSearchText(theCursor.selectedText()) + else: + self.docSearch.setSearchText(None) + self.updateDocMargins() + return + + def beginReplace(self): + """Opens the replace line of the search bar and sets the find + text if a selection has been made, and resets the replace text. + """ + theCursor = self.textCursor() + if theCursor.hasSelection(): + self.docSearch.setSearchText(theCursor.selectedText()) + else: + self.docSearch.setSearchText(None) + self.docSearch.setReplaceText("") + self.updateDocMargins() + return + + def findNext(self, goBack=False): + """Searches for the next or previous occurrence of the search + bar text in the document. Wraps around if not found and loop is + enabled, or continues to next file if next file is enabled. + """ + if not self.anyFocus(): + logger.debug("Editor does not have focus") + return False + + if not self.docSearch.isVisible(): + self.beginSearch() + return + + findOpt = QTextDocument.FindFlag(0) + if goBack: + findOpt |= QTextDocument.FindBackward + if self.docSearch.isCaseSense: + findOpt |= QTextDocument.FindCaseSensitively + if self.docSearch.isWholeWord: + findOpt |= QTextDocument.FindWholeWords + + searchFor = self.docSearch.getSearchObject() + wasFound = self.find(searchFor, findOpt) + if not wasFound: + if self.docSearch.doNextFile and not goBack: + self.theParent.openNextDocument( + self.theHandle, wrapAround=self.docSearch.doLoop + ) + elif self.docSearch.doLoop: + theCursor = self.textCursor() + theCursor.movePosition( + QTextCursor.End if goBack else QTextCursor.Start + ) + self.setTextCursor(theCursor) + wasFound = self.find(searchFor, findOpt) + + if wasFound: + theCursor = self.textCursor() + self.lastFind = (theCursor.selectionStart(), theCursor.selectionEnd()) + + return + + def replaceNext(self): + """Searches for the next occurrence of the search bar text in + the document and replaces it with the replace text. Calls search + next automatically when done. + """ + if not self.anyFocus(): + logger.debug("Editor does not have focus") + return False + + if not self.docSearch.isVisible(): + # The search tool is not active, so we activate it. + self.beginSearch() + return + + theCursor = self.textCursor() + if not theCursor.hasSelection(): + # We have no text selected at all, so just make this a + # regular find next call. + self.findNext() + return + + if self.lastFind is None and theCursor.hasSelection(): + # If we have a selection but no search, it may have been the + # text we triggered the search with, in which case we search + # again from the beginning of that selection to make sure we + # have a valid result. + sPos = theCursor.selectionStart() + theCursor.clearSelection() + theCursor.setPosition(sPos) + self.setTextCursor(theCursor) + self.findNext() + theCursor = self.textCursor() + + if self.lastFind is None: + # In case the above didn't find a result, we give up here. + return + + searchFor = self.docSearch.getSearchText() + replWith = self.docSearch.getReplaceText() + + if self.docSearch.doMatchCap: + replWith = transferCase(theCursor.selectedText(), replWith) + + # Make sure the selected text was selected by an actual find + # call, and not the user. + try: + isFind = self.lastFind[0] == theCursor.selectionStart() + isFind &= self.lastFind[1] == theCursor.selectionEnd() + except Exception: + isFind = False + + if isFind: + theCursor.beginEditBlock() + theCursor.removeSelectedText() + theCursor.insertText(replWith) + theCursor.endEditBlock() + theCursor.setPosition(theCursor.selectionEnd()) + self.setTextCursor(theCursor) + logger.verbose("Replaced occurrence of '%s' with '%s' on line %d" % ( + searchFor, replWith, theCursor.blockNumber() + )) + else: + logger.error("The selected text is not a search result, skipping replace") + + self.findNext() + + return + ## # Internal Functions ## @@ -1585,132 +1718,6 @@ class GuiDocEditor(QTextEdit): self._makeSelection(selMode) return - def _beginSearch(self): - """Sets the selected text as the search text for the search bar. - """ - theCursor = self.textCursor() - if theCursor.hasSelection(): - self.docSearch.setSearchText(theCursor.selectedText()) - else: - self.docSearch.setSearchText(None) - self.updateDocMargins() - return - - def _beginReplace(self): - """Opens the replace line of the search bar and sets the find - text if a selection has been made, and resets the replace text. - """ - theCursor = self.textCursor() - if theCursor.hasSelection(): - self.docSearch.setSearchText(theCursor.selectedText()) - else: - self.docSearch.setSearchText(None) - self.docSearch.setReplaceText("") - self.updateDocMargins() - return - - def _findNext(self, isBackward=False): - """Searches for the next or previous occurrence of the search - bar text in the document. Wraps around if not found and loop is - enabled, or continues to next file if next file is enabled. - """ - if not self.docSearch.isVisible(): - self._beginSearch() - return - - findOpt = QTextDocument.FindFlag(0) - if isBackward: - findOpt |= QTextDocument.FindBackward - if self.docSearch.isCaseSense: - findOpt |= QTextDocument.FindCaseSensitively - if self.docSearch.isWholeWord: - findOpt |= QTextDocument.FindWholeWords - - searchFor = self.docSearch.getSearchObject() - wasFound = self.find(searchFor, findOpt) - if not wasFound: - if self.docSearch.doNextFile and not isBackward: - self.theParent.openNextDocument( - self.theHandle, wrapAround=self.docSearch.doLoop - ) - elif self.docSearch.doLoop: - theCursor = self.textCursor() - theCursor.movePosition( - QTextCursor.End if isBackward else QTextCursor.Start - ) - self.setTextCursor(theCursor) - wasFound = self.find(searchFor, findOpt) - - if wasFound: - theCursor = self.textCursor() - self.lastFind = (theCursor.selectionStart(), theCursor.selectionEnd()) - - return - - def _replaceNext(self): - """Searches for the next occurrence of the search bar text in - the document and replaces it with the replace text. Calls search - next automatically when done. - """ - if not self.docSearch.isVisible(): - # The search tool is not active, so we activate it. - self._beginSearch() - return - - theCursor = self.textCursor() - if not theCursor.hasSelection(): - # We have no text selected at all, so just make this a - # regular find next call. - self._findNext() - return - - if self.lastFind is None and theCursor.hasSelection(): - # If we have a selection but no search, it may have been the - # text we triggered the search with, in which case we search - # again from the beginning of that selection to make sure we - # have a valid result. - sPos = theCursor.selectionStart() - theCursor.clearSelection() - theCursor.setPosition(sPos) - self.setTextCursor(theCursor) - self._findNext() - theCursor = self.textCursor() - - if self.lastFind is None: - # In case the above didn't find a result, we give up here. - return - - searchFor = self.docSearch.getSearchText() - replWith = self.docSearch.getReplaceText() - - if self.docSearch.doMatchCap: - replWith = transferCase(theCursor.selectedText(), replWith) - - # Make sure the selected text was selected by an actual find - # call, and not the user. - try: - isFind = self.lastFind[0] == theCursor.selectionStart() - isFind &= self.lastFind[1] == theCursor.selectionEnd() - except Exception: - isFind = False - - if isFind: - theCursor.beginEditBlock() - theCursor.removeSelectedText() - theCursor.insertText(replWith) - theCursor.endEditBlock() - theCursor.setPosition(theCursor.selectionEnd()) - self.setTextCursor(theCursor) - logger.verbose("Replaced occurrence of '%s' with '%s' on line %d" % ( - searchFor, replWith, theCursor.blockNumber() - )) - else: - logger.error("The selected text is not a search result, skipping replace") - - self._findNext() - - return - def _setupSpellChecking(self): """Create the spell checking object based on the spellTool setting in config. @@ -2074,15 +2081,15 @@ class GuiDocEditSearch(QFrame): """ modKey = qApp.keyboardModifiers() if modKey == Qt.ShiftModifier: - self.docEditor.docAction(nwDocAction.GO_PREV) + self.docEditor.findNext(goBack=True) else: - self.docEditor.docAction(nwDocAction.GO_NEXT) + self.docEditor.findNext() return def _doReplace(self): """Call the replace action function for the document editor. """ - self.docEditor.docAction(nwDocAction.REPL_NEXT) + self.docEditor.replaceNext() return def _doToggleReplace(self, theState): diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 6d6267a9..1673387c 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -756,7 +756,7 @@ class GuiMainMenu(QMenuBar): self.aFind = QAction("Find", self) self.aFind.setStatusTip("Find text in document") self.aFind.setShortcut("Ctrl+F") - self.aFind.triggered.connect(lambda: self._docAction(nwDocAction.FIND)) + self.aFind.triggered.connect(lambda: self.theParent.docEditor.beginSearch()) self.srcMenu.addAction(self.aFind) # Search > Replace @@ -766,7 +766,7 @@ class GuiMainMenu(QMenuBar): self.aReplace.setShortcut("Ctrl+=") else: self.aReplace.setShortcut("Ctrl+H") - self.aReplace.triggered.connect(lambda: self._docAction(nwDocAction.REPLACE)) + self.aReplace.triggered.connect(lambda: self.theParent.docEditor.beginReplace()) self.srcMenu.addAction(self.aReplace) # Search > Find Next @@ -776,7 +776,7 @@ class GuiMainMenu(QMenuBar): self.aFindNext.setShortcuts(["Ctrl+G", "F3"]) else: self.aFindNext.setShortcuts(["F3", "Ctrl+G"]) - self.aFindNext.triggered.connect(lambda: self._docAction(nwDocAction.GO_NEXT)) + self.aFindNext.triggered.connect(lambda: self.theParent.docEditor.findNext()) self.srcMenu.addAction(self.aFindNext) # Search > Find Prev @@ -786,14 +786,14 @@ class GuiMainMenu(QMenuBar): self.aFindPrev.setShortcuts(["Ctrl+Shift+G", "Shift+F3"]) else: self.aFindPrev.setShortcuts(["Shift+F3", "Ctrl+Shift+G"]) - self.aFindPrev.triggered.connect(lambda: self._docAction(nwDocAction.GO_PREV)) + self.aFindPrev.triggered.connect(lambda: self.theParent.docEditor.findNext(goBack=True)) self.srcMenu.addAction(self.aFindPrev) # Search > Replace Next self.aReplaceNext = QAction("Replace Next", self) self.aReplaceNext.setStatusTip("Find and replace next occurrence text in document") self.aReplaceNext.setShortcut("Ctrl+Shift+1") - self.aReplaceNext.triggered.connect(lambda: self._docAction(nwDocAction.REPL_NEXT)) + self.aReplaceNext.triggered.connect(lambda: self.theParent.docEditor.replaceNext()) self.srcMenu.addAction(self.aReplaceNext) return diff --git a/nw/guimain.py b/nw/guimain.py index 6cb68cb3..055a4b00 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -757,14 +757,18 @@ class GuiMain(QMainWindow): return True def passDocumentAction(self, theAction): - """Pass on document action theAction to the document viewer if - it has focus, otherwise pass it to the document editor. + """Pass on document action to the document viewer if it has + focus, or pass it to the document editor if it or any of + its clid widgets have focus. If neither has focus, ignore the + action. """ if self.docViewer.hasFocus(): self.docViewer.docAction(theAction) - else: + elif self.docEditor.hasFocus(): self.docEditor.docAction(theAction) - return True + else: + logger.debug("Action cancelled as neither editor nor viewer has focus") + return ## # Tree Item Actions From c374534d9e0ffaad5b9ef388956dceaef125bd07 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 17 Mar 2021 16:43:51 +0100 Subject: [PATCH 2/6] Fix test --- tests/test_gui/test_gui_doceditor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py index b169e457..dfcf1f86 100644 --- a/tests/test_gui/test_gui_doceditor.py +++ b/tests/test_gui/test_gui_doceditor.py @@ -510,7 +510,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): assert abs(nwGUI.docEditor.getCursorPosition() - 1127) < 3 # Toggle Replace - nwGUI.docEditor._beginReplace() + nwGUI.docEditor.beginReplace() # MonkeyPatch the focus cycle. We can't really test this very well, other than # check that the tabs aren't captured when the main editor has focus From 35855d2d71590897bdafd35134187b00b64ff38f Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 17 Mar 2021 17:09:24 +0100 Subject: [PATCH 3/6] Add a section about search in the docs --- docs/source/usage_interface.rst | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/source/usage_interface.rst b/docs/source/usage_interface.rst index 7947747d..47038ae9 100644 --- a/docs/source/usage_interface.rst +++ b/docs/source/usage_interface.rst @@ -152,6 +152,29 @@ convenient if you want to quickly look through all documents in the list in the pressing :kbd:`F9`. +.. _a_ui_edit_search: + +Search & Replace +---------------- + +The document editor has a search and replace bar that can be activated with :kbd:`Ctrl`:kbd:`F` for +search mode or :kbd:`Ctrl`:kbd:`H` for search/replace mode. + +Pressing :kbd:`Return` while in the search box will search for the next occurrence of the word, and +:kbd:`Shift`:kbd:`Return` for the previous. Pressing :kbd:`Return` in the replace box, will replace +the highlighted text and move to the next word. + +There are a number of settings for the search bar available as toggle switches above the search +box. They allows you to search for, in order:,: matched case only, whole word results only, search +using regular expressions, loop search when reaching the end of the document, and move to the next +document when reaching the end. There is also a switch that will try to match the case of the word +when the replacement is made. That is, it will try to keep the word upper, lower, or capitalised to +match the word being replaced. + +The regular expression search is somewhat dependant on which version of Qt your system has. If you +have Qt 5.13 or higher, there is better support for unicode symbols in the search. + + .. _a_ui_edit_auto: Auto-Replace as You Type @@ -432,7 +455,7 @@ Most features are available as keyboard shortcuts. These are as follows: ":kbd:`Ctrl`:kbd:`Backspace`", "Delete previous word in editor." ":kbd:`Ctrl`:kbd:`'`", "Wrap selected text, or word under cursor, in single quotes." ":kbd:`Ctrl`:kbd:`""`", "Wrap selected text, or word under cursor, in double quotes." - ":kbd:`Ctrl`:kbd:`Enter`", "Open the tag or reference under the cursor in the Viewer." + ":kbd:`Ctrl`:kbd:`Retrun`", "Open the tag or reference under the cursor in the Viewer." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`,`", "Open the :guilabel:`Project Settings` dialog." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`/`", "Remove block formatting for block under cursor." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`1`", "Replace occurrence of search word in current document, and search for next occurrence." From cd1f52c6b5a5210dfcc28d8fbe5c991a1ae0a05c Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 17 Mar 2021 21:05:57 +0100 Subject: [PATCH 4/6] Update translation files --- i18n/nw_en_US.ts | 109 +++++++++++++++++++++++------------------------ i18n/nw_nb_NO.ts | 109 +++++++++++++++++++++++------------------------ i18n/nw_pt.ts | 106 ++++++++++++++++++++++----------------------- 3 files changed, 161 insertions(+), 163 deletions(-) diff --git a/i18n/nw_en_US.ts b/i18n/nw_en_US.ts index 4f725820..e74dc33a 100644 --- a/i18n/nw_en_US.ts +++ b/i18n/nw_en_US.ts @@ -1,6 +1,5 @@ - - + Common @@ -799,22 +798,22 @@ GuiDocEditFooter - + Status - + Line: {0} ({1}) - + Words: {0} ({1}) - + Document size is {0} bytes @@ -822,22 +821,22 @@ GuiDocEditHeader - + Edit document meta - + Search document - + Toggle Focus Mode - + Close the document @@ -845,97 +844,97 @@ GuiDocEditSearch - + Search - + Replace - + Case Sensitive - + Match case - + Whole Words Only - + Match whole words - + RegEx Mode - + Search using regular expressions - + Loop Search - + Loop the search when reaching the end - + Search Next File - + Continue searching in the next file - + Preserve Case - + Preserve case on replace - + Close Search - + Close the search box [{0}] - + Show/hide the replace text box - + Find in current document - + Find and replace in current document @@ -958,72 +957,72 @@ - + File Location - + The currently open file is saved in: - + The document has grown too big and you cannot add more text to it. The maximum size of a single novelWriter document is {0} MB. - + Follow Tag - + Cut - + Copy - + Paste - + Select All - + Select Word - + Select Paragraph - + Spelling Suggestion(s) - + No Suggestions - + Add Word to Dictionary - + Please select some text before calling replace quotes. @@ -1380,7 +1379,7 @@ - + Changes are saved automatically. @@ -1465,57 +1464,57 @@ - + Indexing: '{0}' - + Unknown item - + Indexing completed in {0} ms - + The project index has been successfully rebuilt. - + Information - + Warning - + Error - + This is a bug! - + Internal Error - + Exit - + Do you want to exit novelWriter? diff --git a/i18n/nw_nb_NO.ts b/i18n/nw_nb_NO.ts index c7dfe075..5354295f 100644 --- a/i18n/nw_nb_NO.ts +++ b/i18n/nw_nb_NO.ts @@ -1,6 +1,5 @@ - - + Common @@ -799,22 +798,22 @@ GuiDocEditFooter - + Status Status - + Line: {0} ({1}) Linje: {0} ({1}) - + Words: {0} ({1}) Ord: {0} ({1}) - + Document size is {0} bytes Dokumentet er {0} byte @@ -822,22 +821,22 @@ GuiDocEditHeader - + Edit document meta Rediger dokumentinstillinger - + Search document Søk i dokumentet - + Toggle Focus Mode Slå av/på "Fokus-modus" - + Close the document Lukk dokumentet @@ -845,97 +844,97 @@ GuiDocEditSearch - + Search Søk - + Replace Erstatt - + Case Sensitive Skill store/små bokstaver - + Match case Søket skiller mellom store og små bokstaver - + Whole Words Only Kun hele ord - + Match whole words Søk kun etter hele ord - + RegEx Mode RegEx-modus - + Search using regular expressions Søk ved hjelp av "regular expressions" - + Loop Search Søk rundt - + Loop the search when reaching the end Begynn søket på nytt når enden er nådd - + Search Next File Søk i neste file - + Continue searching in the next file Fortsett søket i neste fil - + Preserve Case Behold store/små bokstaver - + Preserve case on replace Behold store og små bokstaver på samme sted ved erstatt - + Close Search Lukk søk - + Close the search box [{0}] Lukk søkeboksen [{0}] - + Show/hide the replace text box Vis/skjul erstatt-boksen - + Find in current document Søk i det åpne dokumentet - + Find and replace in current document Søk og erstatt i det åpne dokumentet @@ -958,72 +957,72 @@ Stavekontrollen er ferdig - + File Location Filens plassering - + The currently open file is saved in: Det åpne dokumentet er lagret på følgende sted: - + The document has grown too big and you cannot add more text to it. The maximum size of a single novelWriter document is {0} MB. Dokumentet har blitt for stort og du kan ikke legge til mer tekst. Den maksimale tillatte størrelsen for et novelWriter-dokument er {0} MB. - + Follow Tag Følg knagg - + Cut Klipp - + Copy Kopier - + Paste Lim inn - + Select All Velg hele teksten - + Select Word Velg hele ordet - + Select Paragraph Velg hele avsnittet - + Spelling Suggestion(s) Forslag fra stavekontrollen - + No Suggestions Ingen forslag - + Add Word to Dictionary Legg til ord i ordbok - + Please select some text before calling replace quotes. Venligst velg en del av teksten før du velger å erstatte sitattegn. @@ -1375,7 +1374,7 @@ Ønsker du å lukke dette prosjektet? - + Changes are saved automatically. Endringer lagres automatisk. @@ -1460,57 +1459,57 @@ Å importere filen vil overskrive all eksisterende tekst i dokumentet. Ønsker du å fortsette? - + Indexing: '{0}' Indekserer: '{0}' - + Unknown item Ukjent enhet - + Indexing completed in {0} ms Indekseringen tok {0} ms - + The project index has been successfully rebuilt. Prosjektets indeks har blitt bygget på nytt. - + Information Informasjon - + Warning Advarsel - + Error Feil - + This is a bug! Dette er en systemfeil! - + Internal Error Intern feil - + Exit Avslutt - + Do you want to exit novelWriter? Ønsker du å avslutte novelWriter? diff --git a/i18n/nw_pt.ts b/i18n/nw_pt.ts index 7109bb8c..97bc67ff 100644 --- a/i18n/nw_pt.ts +++ b/i18n/nw_pt.ts @@ -798,22 +798,22 @@ GuiDocEditFooter - + Line: {0} ({1}) Linha: {0} ({1}) - + Words: {0} ({1}) Palavras: {0} ({1}) - + Status Estado - + Document size is {0} bytes O tamanho do documento é {0} bytes @@ -821,22 +821,22 @@ GuiDocEditHeader - + Edit document meta Editar os meta-dados do documento - + Search document Procurar no documento - + Toggle Focus Mode Alternar o "Modo Foco" - + Close the document Fechar o documento @@ -844,97 +844,97 @@ GuiDocEditSearch - + Search Pesquisa - + Replace Substituir - + Case Sensitive Diferenciar Maiúsculas e Minúsculas - + Match case Diferencia Maiúsculas e Minúsculas - + Whole Words Only Apenas Palavras Inteiras - + Match whole words Encontra apenas palavras inteiras - + RegEx Mode Expressão Regular - + Loop Search Pesquisa do Início - + Loop the search when reaching the end Pesquisa do início quando chega no final do documento - + Search Next File Busca no Próximo Arquivo - + Continue searching in the next file Continua a busca no próximo arquivo - + Preserve Case Preserva Maiúsculas e Minúsculas - + Preserve case on replace Preserva maiúsculas e minúsculas ao substituir - + Close Search Fechar a Busca - + Show/hide the replace text box Mostrar/Ocultar a caixa substituição - + Find in current document Encontrar no documento atual - + Find and replace in current document Encontrar e substituir no documento atual - + Close the search box [{0}] Fechar a caixa de busca [{0}] - + Search using regular expressions Busca usando expressões regulares @@ -947,7 +947,7 @@ Verificação ortográfica completa - + No Suggestions Sem Sugestões @@ -962,67 +962,67 @@ O texto que você está tentando adicionar é muito grande. O tamanho do texto é {0} MB. O tamanho máximo permitido é {1} MB. - + File Location Localização do Arquivo - + Follow Tag Seguir Etiqueta - + Cut Recortar - + Copy Copiar - + Paste Colar - + Select All Selecionar Tudo - + Select Word Selecionar Palavra - + Select Paragraph Selecionar Parágrafo - + Spelling Suggestion(s) Sugestão de Ortografia - + Add Word to Dictionary Adicionar Palavra ao Dicionário - + Please select some text before calling replace quotes. Por favor, selecione algum texto antes de invocar a substituição de aspas. - + The currently open file is saved in: O arquivo aberto atualmente está salvo em: - + The document has grown too big and you cannot add more text to it. The maximum size of a single novelWriter document is {0} MB. O tamanho do documento aumentou muito e você não pode adicionar mais texto nele. O tamanho máximo de um único documento do novelWriter é {0} MB. @@ -1329,32 +1329,32 @@ Nota: Se o programa ou o computador sofreu uma falha anteriormente, o bloqueio pode ser sobrescrito com segurança. Se, no entanto, outra instância do novelWriter esteja com o projeto aberto, sobrescrever o bloqueio pode corromper o projeto e não é recomendado. - + Unknown item Item desconhecido - + Information Informação - + Warning Alerta - + Error Erro - + This is a bug! Isto é um bug! - + Internal Error Erro Interno @@ -1389,7 +1389,7 @@ Fechar o projeto atual? - + Changes are saved automatically. As alterações serão salvas automaticamente. @@ -1434,22 +1434,22 @@ Importar o arquivo vai sobrescrever o conteúdo atual do documento. Você deseja continuar? - + The project index has been successfully rebuilt. O índice do projeto foi reconstruído com sucesso. - + Exit Sair - + Do you want to exit novelWriter? Você deseja realmente sair do novelWriter? - + Indexing completed in {0} ms Indexação completa em {0} ms @@ -1489,7 +1489,7 @@ O projeto foi bloqueado pelo computador '{0}' ({1} {2}), ativo pela última vez em {3}. - + Indexing: '{0}' Indexando: '{0}' From 0a4e15de31a4fb08a266a9abca88192d0c214ce4 Mon Sep 17 00:00:00 2001 From: jyhelle <47108022+jyhelle@users.noreply.github.com> Date: Wed, 17 Mar 2021 21:06:32 +0100 Subject: [PATCH 5/6] Update French translation submitted by email --- i18n/nw_fr.ts | 296 +++++++++++++++++++++++++------------------------- 1 file changed, 148 insertions(+), 148 deletions(-) diff --git a/i18n/nw_fr.ts b/i18n/nw_fr.ts index f4cd3343..cba98d8e 100644 --- a/i18n/nw_fr.ts +++ b/i18n/nw_fr.ts @@ -293,7 +293,7 @@ Single left-pointing angle quotation mark - guillemet simple vers la gqauche + guillemet simple vers la gauche @@ -371,12 +371,12 @@ novelWriter is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - novelWriter est un logiciel libre: vous pouvez le redistribuer et/ou le modifier selon les termes de la licence publique générale GNU telle que publiée par la Free Software Foundation, dans la version 3 de cette licence ou (à votre choix) dans une version plus récente. + novelWriter est un logiciel libre : vous pouvez le redistribuer et/ou le modifier selon les termes de la licence publique générale GNU telle que publiée par la Free Software Foundation, dans la version 3 de cette licence ou (à votre choix) dans une version plus récente. novelWriter is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - novelWriter est diffusé dans l'espoir qu'il sera utile, mais SANS GARANTIE D'AUCUNE SORTE, en particulier cconcernant sa QUALITÉ MARCHANDE ou son APTITUDE À UN USAGE SPÉCIFIQUE. + novelWriter est diffusé dans l'espoir qu'il sera utile, mais SANS GARANTIE D'AUCUNE SORTE, en particulier concernant sa QUALITÉ MARCHANDE ou son APTITUDE À UN USAGE SPÉCIFIQUE. @@ -401,7 +401,7 @@ Translations - + Traducteurs @@ -416,17 +416,17 @@ Concept - + Concept i18n - + Internationalisation Developer - + Développeur @@ -444,7 +444,7 @@ Formatting Codes: - Codes de mise en forme: + Codes de mise en forme : @@ -564,7 +564,7 @@ Include synopsis - Inclure le synopsys + Inclure le synopsis @@ -704,7 +704,7 @@ Failed to generate preview. The result is too big. - L'aperçu n'a pas été créé. Le résultat est trop volumineux. + L'aperçu n'a pas été créé, le résultat est trop volumineux. @@ -769,7 +769,7 @@ {0} file successfully written to: - Fichier {0} écrit dans: + Fichier {0} écrit dans : @@ -792,28 +792,28 @@ <b>Build Time:</b> {0} - <b>Durée de construction:</b> {0} + <b>Durée de construction :</b> {0} GuiDocEditFooter - + Status État - + Line: {0} ({1}) Ligne: {0} ({1}) - + Words: {0} ({1}) Mots: {0} ({1}) - + Document size is {0} bytes La taille du document est de {0} octets @@ -821,22 +821,22 @@ GuiDocEditHeader - + Edit document meta Modifier les métadonnées du document - + Search document Chercher dans le document - + Toggle Focus Mode Basculer le mode focus - + Close the document Fermer le document @@ -844,97 +844,97 @@ GuiDocEditSearch - + Search Chercher - + Replace Remplacer - + Case Sensitive Sensible à la casse - + Match case Respecter la casse - + Whole Words Only Mots entiers uniquement - + Match whole words Ne vérifier la correspondance que sur des mots entiers - + RegEx Mode Expressions régulières - + Search using regular expressions Chercher en utilisant des expressions régulières - + Loop Search Recherche en boucle - + Loop the search when reaching the end Reprendre la recherche au début du texte lorsque la fin est atteinte - + Search Next File Chercher dans le fichier suivant - + Continue searching in the next file Continuer la recherche dans le fichier suivant - + Preserve Case Conserver la casse - + Preserve case on replace Conserver la casse lors d'un remplacement - + Close Search Terminer la recherche - + Close the search box [{0}] Fermer la boîte de recherche [{0}] - + Show/hide the replace text box Montrer/cacher le texte de remplacement - + Find in current document Chercher dans le document actuel - + Find and replace in current document Chercher et remplacer dans le document actuel @@ -957,72 +957,72 @@ La vérification orthographique est terminée - + File Location Emplacement du fichier - + The currently open file is saved in: - Le fichier actuellement ouvert est enregistré dans: + Le fichier actuellement ouvert est enregistré dans : - + The document has grown too big and you cannot add more text to it. The maximum size of a single novelWriter document is {0} MB. Le document est devenu trop grand et vous ne pouvez plus lui ajouter de texte. La taille maximale d'un fichier novelWriter est de {0} MB. - + Follow Tag Suivre cette étiquette - + Cut Couper - + Copy Copier - + Paste Coller - + Select All Sélectionner tout - + Select Word Sélectionner le mot - + Select Paragraph Sélectionner le paragraphe - + Spelling Suggestion(s) Orthographe suggérée - + No Suggestions Pas de suggestion - + Add Word to Dictionary Ajouter ce mot au dictionnaire - + Please select some text before calling replace quotes. Veuillez sélectionner du texte avant de demander le remplacement des guillemets. @@ -1293,7 +1293,7 @@ Include when building project - À inclure dans la construction du projet + À inclure lors de la construction du projet @@ -1379,7 +1379,7 @@ Fermer le projet en cours ? - + Changes are saved automatically. Les changements sont enregistrés automatiquement. @@ -1411,7 +1411,7 @@ Note: If the program or the computer previously crashed, the lock can safely be overridden. If, however, another instance of novelWriter has the project open, overriding the lock may corrupt the project, and is not recommended. - Note: Si le programme ou l'ordinateur s'est bloqué auparavant, le verrou peut être contourné sans problème. Si par contre le projet est actuellement ouvert par une autre instance de novelWriter, il est déconseillé de contourner le verrou car cela peut corrompre les données du projet. + Note : Si le programme ou l'ordinateur s'est bloqué auparavant, le verrou peut être contourné sans problème. Si par contre le projet est actuellement ouvert par une autre instance de novelWriter, il est déconseillé de contourner le verrou car cela peut corrompre les données du projet. @@ -1464,57 +1464,57 @@ Le contenu du fichier importé va remplacer le contenu actuel du document. Faut-il continuer ? - + Indexing: '{0}' - Indexation de: '{0}' + Indexation d e: '{0}' - + Unknown item Item inconnu - + Indexing completed in {0} ms Indexation effectuée en {0} ms - + The project index has been successfully rebuilt. L'index du projet a été correctement reconstruit. - + Information Information - + Warning Avertissement - + Error Erreur - + This is a bug! Ceci est un bug ! - + Internal Error Erreur interne - + Exit Sortir - + Do you want to exit novelWriter? Voulez-vous sortir de novelWriter ? @@ -2144,7 +2144,7 @@ Insert a non-breaking space - Insérer un espace insécable + Insérer une espace insécable @@ -2647,12 +2647,12 @@ Words: {0} ({1}) - Mots: {0} ({1}) + Mots : {0} ({1}) Project word count (session change) - Nombre de mots du projet (changement par cette session) + Nombre de mots du projet (changement au cours de cette session) @@ -2804,7 +2804,7 @@ Quotes - + Guillemets @@ -2882,37 +2882,37 @@ Automatic Padding - + Insertion automatique Insert non-breaking space before - + Espace insécable avant Insert non-breaking space after - + Espace insécable après Use thin space instead - + Utiliser des espaces fines Automatically add space before any of these symbols. - + Ajouter lors de la frappe une espace avant chacun de ces caractères. Automatically add space after any of these symbols. - + Ajouter lors de la frappe une espace après chacun de ces caractères. Inserts a thin space instead of a regular space. - + Insérer une espace fine au lieu d'une espace-mot. @@ -3068,7 +3068,7 @@ kB - kO + ko @@ -3211,7 +3211,7 @@ Show full path in document header - Montrer le chemin complet dans l'e-tête du document + Montrer le chemin complet dans l'en-tête du document @@ -3284,7 +3284,7 @@ Path: {0} - Chemin: {0} + Chemin : {0} @@ -3329,7 +3329,7 @@ User activity includes typing and changing the content. - L'activité de lútilisateur inclut la frappe et la modification du contenu. + L'activité de l'utilisateur inclut la frappe et la modification du contenu. @@ -3521,7 +3521,7 @@ Assume a new chapter or partition always start on an odd numbered page. - On suppose qu'un nouveau chapitre ou une nouvelle partiedébute sur une page de droite, numérotée impaire. + On suppose qu'un nouveau chapitre ou une nouvelle partie débute sur une page de droite, numérotée impaire. @@ -3554,7 +3554,7 @@ Working Title: {0} - Titre de travail: {0} + Titre de travail : {0} @@ -3685,7 +3685,7 @@ Select item to edit - + Choisir l'élément à éditer @@ -3728,7 +3728,7 @@ Usage - + Utilisation @@ -3743,27 +3743,27 @@ Select item to edit - + Choisir l'élément à éditer Cannot delete a status item that is in use. - + On ne peut pas retirer un élément tant qu'il est utilisé. Not in use - + Inutilisé Used once - + Utilisé une fois Used by {0} items - + Utilisé {0} fois @@ -4129,32 +4129,32 @@ Total Time: - Temps total: + Temps total : Idle Time: - Temps d'inactivité: + Temps d'inactivité : Filtered Time: - Temps après filtrage: + Temps après filtrage : Novel Word Count: - Compte de mots du texte: + Compte de mots du texte : Notes Word Count: - Compte de mot des notes: + Compte de mot des notes : Total Word Count: - Compte de mots total: + Compte de mots total : @@ -4242,7 +4242,7 @@ Opened Document: {0} - Document ouvert: {0} + Document ouvert : {0} @@ -4252,7 +4252,7 @@ Saved Document: {0} - Document enregistré: {0} + Document enregistré : {0} @@ -4365,7 +4365,7 @@ File not found: {0} - Fichier introuvable: {0} + Fichier introuvable : {0} @@ -4390,7 +4390,7 @@ Unknown or unsupported novelWriter project file format. The project cannot be opened by this version of novelWriter. The file was saved with novelWriter version {0}. - Format de projet novelWriter inconnu ou non supporté. Ce projet ne peut pas être ouvert avec cette version de novelWrter, il a été enregistré avec novelWriter version {0}. + Format de projet novelWriter inconnu ou non supporté. Ce projet ne peut pas être ouvert avec cette version de novelWriter, il a été enregistré avec novelWriter version {0}. @@ -4405,7 +4405,7 @@ Opened Project: {0} - Projet ouvert: {0} + Projet ouvert : {0} @@ -4420,12 +4420,12 @@ Saved Project: {0} - Projet enregistré: {0} + Projet enregistré : {0} Backing up project ... - Sauvegarde du projett en cours ... + Sauvegarde du projet en cours ... @@ -4460,7 +4460,7 @@ Backup archive file written to: {0} - Archivage du fichier de sauvegarde effectué en: {0} + Archivage du fichier de sauvegarde effectué en : {0} @@ -4510,7 +4510,7 @@ Found {0} orphaned file(s) in project folder. - Trouvé {0} fichier(s) orphelin8s) dans le dossier du projet. + Trouvé {0} fichier(s) orphelin(s) dans le dossier du projet. @@ -4535,22 +4535,22 @@ Not a folder: {0} - Pas un dossier: {0} + Pas un dossier : {0} Could not move: {0} - Pas pu déplacer: {0} + Pas pu déplacer : {0} Could not delete: {0} - Pas pu effacer: {0} + Pas pu effacer : {0} Could not make folder: {0} - Pas pu crér le dossier: {0} + Pas pu crér le dossier : {0} @@ -4662,7 +4662,7 @@ Provide at least a working title. The working title should not be change beyond this point as it is used by the application for generating file names for for instance backups. The other fields are optional and can be changed at any time in Project Settings. - Donnez au moins un titre de travail. Ce titre de travail ne devrait plus être modifié ensuite car il sert de référence à l'application pour la création des noms de fichier par exemple lors des sauvegardes. Les autres champs sont optionnels et peuvent être changés ultérieurement dans les paramètres du projet. + Donnez au moins un titre de travail. Ce titre de travail ne devrait plus être modifié ensuite car il sert de référence à l'application pour la création des noms de fichier, par exemple lors des sauvegardes. Les autres champs sont optionnels et peuvent être changés ultérieurement dans les paramètres du projet. @@ -4733,7 +4733,7 @@ OK - + OK @@ -4741,27 +4741,27 @@ &OK - + &OK &Save - + &Sauvegarder &Cancel - + &Annuler &Close - + &Fermer Close without Saving - + Fermer sans sauvegarder @@ -4769,92 +4769,92 @@ OK - + OK Save - + Sauvegarder Save All - + Tout sauvegarder Open - + Ouvrir &Yes - + &Oui Yes to &All - + Oui à &Tout &No - + &Non N&o to All - + N&on à Tout Abort - + Abandonner Retry - + Essayer de nouveau Ignore - + Ignorer Close - + Fermer Cancel - + Annuler Discard - + Éliminer Help - + Aide Apply - + Appliquer Reset - + Réinitialiser Restore Defaults - + Retour aux valeurs initiales @@ -4862,57 +4862,57 @@ Go Back - + Revenir < &Back - + < &Reculer Continue - + Continuer &Next - + &Suivant &Next > - + &Suivant > Commit - + Valider Done - + Terminé &Finish - + &Terminer Cancel - + Annuler Help - + Aide &Help - + &Aide From 86e196808eb2325f5388ae7c904e13c61adbdbd4 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 17 Mar 2021 21:06:58 +0100 Subject: [PATCH 6/6] Fix bug saving quote settings in Preferences --- nw/gui/preferences.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index f36dec8a..529c71bc 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -99,6 +99,7 @@ class GuiPreferences(PagedDialog): self.tabEditor.saveValues() self.tabSyntax.saveValues() self.tabAuto.saveValues() + self.tabQuote.saveValues() if needsRestart: self.theParent.makeAlert(