diff --git a/src/mainWindow.py b/src/mainWindow.py index c1482cd2..3cf98dae 100644 --- a/src/mainWindow.py +++ b/src/mainWindow.py @@ -15,7 +15,16 @@ from models.outlineModel import * from models.persosProxyModel import * from functions import * +# Spell checker support +try: + import enchant +except ImportError: + enchant = None + class MainWindow(QMainWindow, Ui_MainWindow): + + dictChanged = pyqtSignal(unicode) + def __init__(self): QMainWindow.__init__(self) self.setupUi(self) @@ -186,7 +195,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.mprOutline = QDataWidgetMapper() self.mprOutline.setModel(self.mdlOutline) mapping = [ - (self.redacEditor.txtRedacText, Outline.text.value), (self.txtRedacSummarySentance, Outline.summarySentance.value), (self.txtRedacSummaryFull, Outline.summaryFull.value), (self.txtRedacNotes, Outline.notes.value), @@ -202,6 +210,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.treeRedacOutline.selectionModel().currentChanged.connect(self.cmbRedacStatus.setCurrentModelIndex) self.treeRedacOutline.selectionModel().currentChanged.connect(self.chkRedacCompile.setCurrentModelIndex) self.treeRedacOutline.selectionModel().currentChanged.connect(self.redacEditor.setCurrentModelIndex) + self.treeRedacOutline.selectionModel().currentChanged.connect(self.redacEditor.txtRedacText.setCurrentModelIndex) + self.tabMain.currentChanged.connect(self.mprOutline.submit) self.treeRedacOutline.selectionModel().currentChanged.connect(self.outlineSelectionChanged) @@ -425,7 +435,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.splitterRedac.setStretchFactor(2, 20) # Help box - references = [ (self.lytTabOverview, "Entrez toutes les informations relatives au livre, ainsi qu'à vous."), @@ -448,4 +457,40 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.actShowHelp.toggled.connect(label.setVisible) widget.layout().insertWidget(0, label) - self.actShowHelp.setChecked(False) \ No newline at end of file + self.actShowHelp.setChecked(False) + + # Spellcheck + if enchant: + self.menuDict = QMenu("Dictionary") + self.menuDictGroup = QActionGroup(self) + + for i in enchant.list_dicts(): + a = QAction(unicode(i[0]), self) + a.setCheckable(True) + a.triggered.connect(self.setDictionary) + if unicode(i[0]) == enchant.get_default_language(): # "fr_CH" + a.setChecked(True) + self.menuDictGroup.addAction(a) + self.menuDict.addAction(a) + + self.menuTools.addMenu(self.menuDict) + + self.actSpellcheck.toggled.connect(self.redacEditor.toggleSpellcheck) + self.dictChanged.connect(self.redacEditor.setDict) + + + else: + # No Spell check support + self.actSpellcheck.setVisible(False) + a = QAction("Install PyEnchant to use spellcheck", self) + a.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxWarning)) + a.triggered.connect(self.openPyEnchantWebPage) + self.menuTools.addAction(a) + + def setDictionary(self): + for i in self.menuDictGroup.actions(): + if i.isChecked(): + self.dictChanged.emit(i.text().replace("&", "")) + + def openPyEnchantWebPage(self): + QDesktopServices.openUrl(QUrl("http://pythonhosted.org/pyenchant/")) \ No newline at end of file diff --git a/src/models/outlineModel.py b/src/models/outlineModel.py index 5195d3bf..f198e275 100644 --- a/src/models/outlineModel.py +++ b/src/models/outlineModel.py @@ -411,10 +411,6 @@ class outlineItem(): if column == Outline.text.value and self.isFolder(): # Folder have no text return - - if column == Outline.text.value: - wc = wordCount(data) - self.setData(Outline.wordCount.value, wc) if column == Outline.goal.value: self._data[Outline.setGoal] = toInt(data) if toInt(data) > 0 else "" @@ -425,6 +421,10 @@ class outlineItem(): self._data[Outline(column)] = data + if column == Outline.text.value: + wc = wordCount(data) + self.setData(Outline.wordCount.value, wc) + if updateWordCount: self.updateWordCount() diff --git a/src/ui/editors/customTextEdit.py b/src/ui/editors/customTextEdit.py index 13b529ee..550f8698 100644 --- a/src/ui/editors/customTextEdit.py +++ b/src/ui/editors/customTextEdit.py @@ -14,18 +14,141 @@ except ImportError: class customTextEdit(QTextEdit): - def __init__(self, parent=None): + def __init__(self, parent=None, index=None, html=None, spellcheck=True, dict=""): QTextEdit.__init__(self, parent) + self.document().contentsChanged.connect(self.sizeChange) - self.defaultFontPointSize = 9 - self.highlightWord = "" - self.highligtCS = False - - self.highlighter = t2tHighlighter(self) + self.currentIndex = None - # Spellchecking - if enchant: - self.dict = enchant.Dict("fr_CH") - self.spellcheck = True - else: - self.spellcheck = False \ No newline at end of file + self.heightMin = 0 + self.heightMax = 65000 + self.sizeChange() + self.item = None + self.spellcheck = spellcheck + self.currentDict = dict + + if index: + self.setCurrentModelIndex(index) + + elif html: + self.document().setHtml(html) + self.setReadOnly(True) + + def setCurrentModelIndex(self, index): + if index.isValid(): + self.currentIndex = index + self.item = index.internalPointer() + self._model = index.model() + self.document().contentsChanged.connect(self.submit) + self._model.dataChanged.connect(self.update) + self.updateText() + + self.defaultFontPointSize = qApp.font().pointSize() + self.highlightWord = "" + self.highligtCS = False + + self.highlighter = t2tHighlighter(self) + + # Spellchecking + if enchant and self.spellcheck: + self.dict = enchant.Dict(self.currentDict if self.currentDict else enchant.get_default_language()) + + def submit(self): + if self.toPlainText() <> self.item.data(Outline.text.value): + #self._model.setData(self.item.index(), self.toPlainText(), Outline.text.value) + self.item.setData(Outline.text.value, self.toPlainText()) + + def update(self, topLeft, bottomRight): + if topLeft.row() <= self.currentIndex.row() <= bottomRight.row(): + self.updateText() + + def updateText(self): + if self.item: + if self.toPlainText() <> self.item.data(Outline.text.value): + self.document().setPlainText(self.item.data(Outline.text.value)) + + def resizeEvent(self, e): + QTextEdit.resizeEvent(self, e) + self.sizeChange() + + def sizeChange(self): + docHeight = self.document().size().height() + if self.heightMin <= docHeight <= self.heightMax: + self.setMinimumHeight(docHeight) + + + # ----------------------------------------------------------------------------------------------------- + # Spellchecking based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/ + + def setDict(self, d): + self.dict = enchant.Dict(d) + self.highlighter.rehighlight() + + def toggleSpellcheck(self, v): + self.spellcheck = v + self.highlighter.rehighlight() + + def mousePressEvent(self, event): + if event.button() == Qt.RightButton: + # Rewrite the mouse event to a left button event so the cursor is + # moved to the location of the pointer. + event = QMouseEvent(QEvent.MouseButtonPress, event.pos(), + Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) + QTextEdit.mousePressEvent(self, event) + + class SpellAction(QAction): + "A special QAction that returns the text in a signal. Used for spellckech." + + correct = pyqtSignal(unicode) + + def __init__(self, *args): + QAction.__init__(self, *args) + + self.triggered.connect(lambda x: self.correct.emit( + unicode(self.text()))) + + def contextMenuEvent(self, event): + # Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/ + + if not self.spellcheck: + QTextEdit.contextMenuEvent(self, event) + return + + popup_menu = self.createStandardContextMenu() + + # Select the word under the cursor. + cursor = self.textCursor() + cursor.select(QTextCursor.WordUnderCursor) + self.setTextCursor(cursor) + + # Check if the selected word is misspelled and offer spelling + # suggestions if it is. + if self.textCursor().hasSelection(): + text = unicode(self.textCursor().selectedText()) + if not self.dict.check(text): + spell_menu = QMenu('Spelling Suggestions') + for word in self.dict.suggest(text): + action = self.SpellAction(word, spell_menu) + action.correct.connect(self.correctWord) + spell_menu.addAction(action) + # Only add the spelling suggests to the menu if there are + # suggestions. + if len(spell_menu.actions()) != 0: + popup_menu.insertSeparator(popup_menu.actions()[0]) + popup_menu.insertMenu(popup_menu.actions()[0], spell_menu) + + popup_menu.exec_(event.globalPos()) + + def correctWord(self, word): + ''' + Replaces the selected text with word. + ''' + cursor = self.textCursor() + cursor.beginEditBlock() + + cursor.removeSelectedText() + cursor.insertText(word) + + cursor.endEditBlock() + + # ----------------------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/ui/editors/editorWidget.py b/src/ui/editors/editorWidget.py index 55defd02..fd6574ca 100644 --- a/src/ui/editors/editorWidget.py +++ b/src/ui/editors/editorWidget.py @@ -7,61 +7,24 @@ from __future__ import unicode_literals from qt import * from enums import * from ui.editors.editorWidget_ui import * - -class GrowingTextEdit(QTextEdit): - - def __init__(self, index=None, html=None, parent=None): - QTextEdit.__init__(self, parent) - self.document().contentsChanged.connect(self.sizeChange) - - self.heightMin = 0 - self.heightMax = 65000 - self.sizeChange() - self.item = None - - if index: - self.currentIndex = index - self.item = index.internalPointer() - self._model = index.model() - - self._model.dataChanged.connect(self.update) - self.document().contentsChanged.connect(self.submit) - - else: - self.document().setHtml(html) - self.setReadOnly(True) - - self.updateText() - - def submit(self): - self.item.setData(Outline.text.value, self.toPlainText()) - - def update(self, topLeft, bottomRight): - if topLeft.row() <= self.currentIndex.row() <= bottomRight.row(): - self.updateText() - - def updateText(self): - if self.item: - self.document().setPlainText(self.item.data(Outline.text.value)) - - def resizeEvent(self, e): - QTextEdit.resizeEvent(self, e) - self.sizeChange() - - def sizeChange(self): - docHeight = self.document().size().height() - if self.heightMin <= docHeight <= self.heightMax: - self.setMinimumHeight(docHeight) +from ui.editors.customTextEdit import * class editorWidget(QWidget, Ui_editorWidget_ui): + toggledSpellcheck = pyqtSignal(bool) + dictChanged = pyqtSignal(str) + def __init__(self, parent=None): QWidget.__init__(self, parent) self.setupUi(self) self.currentIndex = None self.txtEdits = [] self.scroll.setBackgroundRole(QPalette.Base) + self.toggledSpellcheck.connect(self.txtRedacText.toggleSpellcheck) + self.dictChanged.connect(self.txtRedacText.setDict) + self.currentDict = "" + self.spellcheck = True def setCurrentModelIndex(self, index): @@ -82,7 +45,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui): self.txtEdits = [] def addTitle(itm): - edt = GrowingTextEdit(html="{t}".format(l=min(itm.level()+1, 5), t=itm.title())) + edt = customTextEdit(self, html="{t}".format(l=min(itm.level()+1, 5), t=itm.title())) edt.setFrameShape(QFrame.NoFrame) self.txtEdits.append(edt) l.addWidget(edt) @@ -94,9 +57,11 @@ class editorWidget(QWidget, Ui_editorWidget_ui): l.addWidget(line) def addScene(itm): - edt = GrowingTextEdit(index=itm.index()) + edt = customTextEdit(self, index=itm.index(), spellcheck=self.spellcheck, dict=self.currentDict) edt.setFrameShape(QFrame.NoFrame) edt.setStatusTip(itm.path()) + self.toggledSpellcheck.connect(edt.toggleSpellcheck) + self.dictChanged.connect(edt.setDict) #edt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.txtEdits.append(edt) l.addWidget(edt) @@ -125,4 +90,11 @@ class editorWidget(QWidget, Ui_editorWidget_ui): else: self.currentIndex = None - \ No newline at end of file + + def toggleSpellcheck(self, v): + self.spellcheck = v + self.toggledSpellcheck.emit(v) + + def setDict(self, dct): + self.currentDict = dct + self.dictChanged.emit(dct) \ No newline at end of file diff --git a/src/ui/editors/t2tHighlighter.py b/src/ui/editors/t2tHighlighter.py index 50471677..113740e9 100644 --- a/src/ui/editors/t2tHighlighter.py +++ b/src/ui/editors/t2tHighlighter.py @@ -343,13 +343,14 @@ class t2tHighlighter (QSyntaxHighlighter): data = blockUserData.getUserData(block) # Header Lines - if block.blockNumber() == 0: - block.setUserState(State.HEADER_LINE) - return - elif block.blockNumber() in [1, 2] and \ - self.document().findBlockByNumber(0).text(): - block.setUserState(State.HEADER_LINE) - return + # No header line here + #if block.blockNumber() == 0: + #block.setUserState(State.HEADER_LINE) + #return + #elif block.blockNumber() in [1, 2] and \ + #self.document().findBlockByNumber(0).text(): + #block.setUserState(State.HEADER_LINE) + #return state = 0 inList = False diff --git a/src/ui/editors/t2tHighlighterStyle.py b/src/ui/editors/t2tHighlighterStyle.py index 3b3c4eab..df6e334f 100644 --- a/src/ui/editors/t2tHighlighterStyle.py +++ b/src/ui/editors/t2tHighlighterStyle.py @@ -21,11 +21,11 @@ class t2tHighlighterStyle (): # Defaults self.defaultFontPointSize = self.editor.defaultFontPointSize - self.defaultFontFamily = "" + self.defaultFontFamily = qApp.font().family() self.tabStopWidth = 40 self.setupEditor() - + if self.name == "Default": self.initDefaults() #Temporary other theme @@ -126,7 +126,7 @@ class t2tHighlighterStyle (): blockFormat.setBackground(QColor("#EEEEFA")) n = blockUserData.getUserData(block).leadingSpaces() + 1 f = QFontMetrics(QFont(self.defaultFontFamily, - self.defaultFontPointSize)) + self.defaultFontPointSize)) fm = f.width(" " * n + blockUserData.getUserData(block).listSymbol()) blockFormat.setTextIndent(-fm) @@ -170,7 +170,7 @@ class t2tHighlighterStyle (): if preset in [State.HEADER_LINE]: size = size * 2 - print size + #print size if preset in [State.RAW_AREA, State.RAW_LINE, "raw"]: color = "blue" diff --git a/src/ui/mainWindow.py b/src/ui/mainWindow.py index 34460344..054f6be4 100644 --- a/src/ui/mainWindow.py +++ b/src/ui/mainWindow.py @@ -1051,6 +1051,8 @@ class Ui_MainWindow(object): self.menuMode.setObjectName("menuMode") self.menu_Aide = QtWidgets.QMenu(self.menubar) self.menu_Aide.setObjectName("menu_Aide") + self.menuTools = QtWidgets.QMenu(self.menubar) + self.menuTools.setObjectName("menuTools") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") @@ -1089,6 +1091,14 @@ class Ui_MainWindow(object): icon = QtGui.QIcon.fromTheme("system-help") self.actShowHelp.setIcon(icon) self.actShowHelp.setObjectName("actShowHelp") + self.actSpellcheck = QtWidgets.QAction(MainWindow) + self.actSpellcheck.setCheckable(True) + self.actSpellcheck.setChecked(True) + icon = QtGui.QIcon.fromTheme("tools-check-spelling") + self.actSpellcheck.setIcon(icon) + self.actSpellcheck.setObjectName("actSpellcheck") + self.actSpellcheckDict = QtWidgets.QAction(MainWindow) + self.actSpellcheckDict.setObjectName("actSpellcheckDict") self.menu_Fichier.addAction(self.actionNouveau) self.menu_Fichier.addAction(self.actionOuvrir) self.menu_Fichier.addAction(self.action_R_cents) @@ -1098,9 +1108,11 @@ class Ui_MainWindow(object): self.menu_Fichier.addAction(self.actionQuitter) self.menuMode.addAction(self.actionSnowflakeMode) self.menu_Aide.addAction(self.actShowHelp) + self.menuTools.addAction(self.actSpellcheck) self.menubar.addAction(self.menu_Fichier.menuAction()) self.menubar.addAction(self.menuMode.menuAction()) self.menubar.addAction(self.menu_Aide.menuAction()) + self.menubar.addAction(self.menuTools.menuAction()) self.retranslateUi(MainWindow) self.tabMain.setCurrentIndex(6) @@ -1257,6 +1269,7 @@ class Ui_MainWindow(object): self.menu_Fichier.setTitle(_translate("MainWindow", "&Fichier")) self.menuMode.setTitle(_translate("MainWindow", "Mo&de")) self.menu_Aide.setTitle(_translate("MainWindow", "&Aide")) + self.menuTools.setTitle(_translate("MainWindow", "&Outlis")) self.actionOuvrir.setText(_translate("MainWindow", "&Ouvrir")) self.actionOuvrir.setShortcut(_translate("MainWindow", "Ctrl+O")) self.action_R_cents.setText(_translate("MainWindow", "&Récents")) @@ -1272,10 +1285,13 @@ class Ui_MainWindow(object): self.actionNouveau.setShortcut(_translate("MainWindow", "Ctrl+N")) self.actShowHelp.setText(_translate("MainWindow", "Afficher les &bulles d\'aide")) self.actShowHelp.setShortcut(_translate("MainWindow", "Ctrl+Shift+B")) + self.actSpellcheck.setText(_translate("MainWindow", "Correcteur ortographique")) + self.actSpellcheck.setShortcut(_translate("MainWindow", "F8")) + self.actSpellcheckDict.setText(_translate("MainWindow", "Dictionnaire")) from ui.cmbOutlineStatusChoser import cmbOutlineStatusChoser -from ui.cmbOutlinePersoChoser import cmbOutlinePersoChoser -from ui.collapsibleGroupBox2 import collapsibleGroupBox2 -from ui.chkOutlineCompile import chkOutlineCompile from ui.sldImportance import sldImportance from ui.editors.editorWidget import editorWidget +from ui.cmbOutlinePersoChoser import cmbOutlinePersoChoser +from ui.chkOutlineCompile import chkOutlineCompile +from ui.collapsibleGroupBox2 import collapsibleGroupBox2 diff --git a/src/ui/mainWindow.ui b/src/ui/mainWindow.ui index b328e988..de7424fe 100644 --- a/src/ui/mainWindow.ui +++ b/src/ui/mainWindow.ui @@ -2046,9 +2046,16 @@ + + + &Outlis + + + + @@ -2159,6 +2166,28 @@ Ctrl+Shift+B + + + true + + + true + + + + + + Correcteur ortographique + + + F8 + + + + + Dictionnaire + + diff --git a/test_project/outline.xml b/test_project/outline.xml index fb53a2da..c8883260 100644 --- a/test_project/outline.xml +++ b/test_project/outline.xml @@ -1,25 +1,25 @@ - + - - - + + + - - + + - - + + - +