Index: compile_rules.py ================================================================== --- compile_rules.py +++ compile_rules.py @@ -432,17 +432,23 @@ lLine = [] lRuleLine = [] lTest = [] lOpt = [] zBookmark = re.compile("^!!+") + zGraphLink = re.compile(r"^@@@@GRAPHLINK>(\w+)@@@@") for i, sLine in enumerate(lRules, 1): if sLine.startswith('#END'): printBookmark(0, "BREAK BY #END", i) break elif sLine.startswith("#"): pass + elif sLine.startswith("@@@@"): + m = re.match(r"^@@@@GRAPHLINK>(\w+)@@@@", sLine.strip()) + if m: + #lRuleLine.append(["@GRAPHLINK", m.group(1)]) + printBookmark(1, "@GRAPHLINK: " + m.group(1), i) elif sLine.startswith("DEF:"): m = re.match("DEF: +([a-zA-Z_][a-zA-Z_0-9]*) +(.+)$", sLine.strip()) if m: dDEF["{"+m.group(1)+"}"] = m.group(2) else: Index: gc_core/py/__init__.py ================================================================== --- gc_core/py/__init__.py +++ gc_core/py/__init__.py @@ -0,0 +1,2 @@ + +from .grammar_checker import * ADDED gc_core/py/grammar_checker.py Index: gc_core/py/grammar_checker.py ================================================================== --- gc_core/py/grammar_checker.py +++ gc_core/py/grammar_checker.py @@ -0,0 +1,73 @@ +# Grammalecte +# Main class: wrapper + +import importlib +import json + +from . import text + + +class GrammarChecker: + + def __init__ (self, sLangCode, sContext="Python"): + self.sLangCode = sLangCode + # Grammar checker engine + self.gce = importlib.import_module("."+sLangCode, "grammalecte") + self.gce.load(sContext) + # Spell checker + self.oSpellChecker = self.gce.getSpellChecker() + # Lexicographer + self.oLexicographer = None + # Text formatter + self.oTextFormatter = None + + def getGCEngine (self): + return self.gce + + def getSpellChecker (self): + return self.oSpellChecker + + def getTextFormatter (self): + if self.oTextFormatter == None: + self.tf = importlib.import_module("."+self.sLangCode+".textformatter", "grammalecte") + self.oTextFormatter = self.tf.TextFormatter() + return self.oTextFormatter + + def getLexicographer (self): + if self.oLexicographer == None: + self.lxg = importlib.import_module("."+self.sLangCode+".lexicographe", "grammalecte") + self.oLexicographer = self.lxg.Lexicographe(self.oSpellChecker) + return self.oLexicographer + + def displayGCOptions (self): + self.gce.displayOptions() + + def getParagraphErrors (self, sText, dOptions=None, bContext=False, bSpellSugg=False, bDebug=False): + "returns a tuple: (grammar errors, spelling errors)" + aGrammErrs = self.gce.parse(sText, "FR", bDebug=bDebug, dOptions=dOptions, bContext=bContext) + aSpellErrs = self.oSpellChecker.parseParagraph(sText, bSpellSugg) + return aGrammErrs, aSpellErrs + + def generateText (self, sText, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False): + pass + + def generateTextAsJSON (self, sText, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, bDebug=False): + pass + + def generateParagraph (self, sText, dOptions=None, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False): + aGrammErrs, aSpellErrs = self.getParagraphErrors(sText, dOptions, False, bSpellSugg, bDebug) + if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs: + return "" + return text.generateParagraph(sText, aGrammErrs, aSpellErrs, nWidth) + + def generateParagraphAsJSON (self, iIndex, sText, dOptions=None, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, lLineSet=None, bDebug=False): + aGrammErrs, aSpellErrs = self.getParagraphErrors(sText, dOptions, bContext, bSpellSugg, bDebug) + aGrammErrs = list(aGrammErrs) + if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs: + return "" + if lLineSet: + aGrammErrs, aSpellErrs = text.convertToXY(aGrammErrs, aSpellErrs, lLineSet) + return json.dumps({ "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False) + if bReturnText: + return json.dumps({ "iParagraph": iIndex, "sText": sText, "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False) + return json.dumps({ "iParagraph": iIndex, "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False) Index: gc_lang/fr/config.ini ================================================================== --- gc_lang/fr/config.ini +++ gc_lang/fr/config.ini @@ -92,10 +92,13 @@ # TextFormatter oxt/TextFormatter/TextFormatter.py = pythonpath/TextFormatter.py oxt/TextFormatter/tf_strings.py = pythonpath/tf_strings.py oxt/TextFormatter/tf_options.py = pythonpath/tf_options.py oxt/TextFormatter/tf_tabrep.py = pythonpath/tf_tabrep.py +# Lexicographer +oxt/Lexicographer/Enumerator.py = pythonpath/Enumerator.py +oxt/Lexicographer/enum_strings.py = pythonpath/enum_strings.py # Conjugueur oxt/Conjugueur/Conjugueur.py = pythonpath/Conjugueur.py # Modify author oxt/ChangeAuthor/Author.py = pythonpath/Author.py oxt/ChangeAuthor/ca_strings.py = pythonpath/ca_strings.py Index: gc_lang/fr/oxt/AppLauncher.py ================================================================== --- gc_lang/fr/oxt/AppLauncher.py +++ gc_lang/fr/oxt/AppLauncher.py @@ -56,10 +56,14 @@ xDialog.run(self.sLang) elif sCmd == "OP": import Options xDialog = Options.GC_Options(self.ctx) xDialog.run(self.sLang) + elif sCmd == "EN": + import Enumerator + xDialog = Enumerator.Enumerator(self.ctx) + xDialog.run(self.sLang) elif sCmd.startswith("FA/"): findAll(sCmd[6:], (sCmd[3:4] == "y"), (sCmd[4:5] == "y")) # elif sCmd.startswith("URL/"): # # Call from context menu to launch URL? # # http://opengrok.libreoffice.org/xref/core/sw/source/ui/lingu/olmenu.cxx#785 ADDED gc_lang/fr/oxt/Lexicographer/Enumerator.py Index: gc_lang/fr/oxt/Lexicographer/Enumerator.py ================================================================== --- gc_lang/fr/oxt/Lexicographer/Enumerator.py +++ gc_lang/fr/oxt/Lexicographer/Enumerator.py @@ -0,0 +1,304 @@ +# Dictionary Options +# by Olivier R. +# License: MPL 2 + +import unohelper +import uno +import traceback + +import helpers +import enum_strings +import grammalecte.graphspell as sc + +from com.sun.star.task import XJobExecutor +from com.sun.star.awt import XActionListener +from com.sun.star.beans import PropertyValue + + +def hexToRBG (sHexa): + r = int(sHexa[:2], 16) + g = int(sHexa[2:4], 16) + b = int(sHexa[4:], 16) + return (r & 255) << 16 | (g & 255) << 8 | (b & 255) + + +def _waitPointer (funcDecorated): + def wrapper (*args, **kwargs): + # self is the first parameter if the decorator is applied on a object + self = args[0] + # before + xPointer = self.xSvMgr.createInstanceWithContext("com.sun.star.awt.Pointer", self.ctx) + xPointer.setType(uno.getConstantByName("com.sun.star.awt.SystemPointer.WAIT")) + xWindowPeer = self.xContainer.getPeer() + xWindowPeer.setPointer(xPointer) + for x in xWindowPeer.Windows: + x.setPointer(xPointer) + # processing + result = funcDecorated(*args, **kwargs) + # after + xPointer.setType(uno.getConstantByName("com.sun.star.awt.SystemPointer.ARROW")) + xWindowPeer.setPointer(xPointer) + for x in xWindowPeer.Windows: + x.setPointer(xPointer) + self.xContainer.setVisible(True) # seems necessary to refresh the dialog box and text widgets (why?) + # return + return result + return wrapper + + +class Enumerator (unohelper.Base, XActionListener, XJobExecutor): + + def __init__ (self, ctx): + self.ctx = ctx + self.xSvMgr = self.ctx.ServiceManager + self.xDesktop = self.xSvMgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.ctx) + self.xDocument = self.xDesktop.getCurrentComponent() + self.xContainer = None + self.xDialog = None + self.oSpellChecker = None + + def _addWidget (self, name, wtype, x, y, w, h, **kwargs): + xWidget = self.xDialog.createInstance('com.sun.star.awt.UnoControl%sModel' % wtype) + xWidget.Name = name + xWidget.PositionX = x + xWidget.PositionY = y + xWidget.Width = w + xWidget.Height = h + for k, w in kwargs.items(): + setattr(xWidget, k, w) + self.xDialog.insertByName(name, xWidget) + return xWidget + + def _addGrid (self, name, x, y, w, h, columns, **kwargs): + xGridModel = self.xDialog.createInstance('com.sun.star.awt.grid.UnoControlGridModel') + xGridModel.Name = name + xGridModel.PositionX = x + xGridModel.PositionY = y + xGridModel.Width = w + xGridModel.Height = h + xColumnModel = xGridModel.ColumnModel + for e in columns: + xCol = xColumnModel.createColumn() + for k, w in e.items(): + setattr(xCol, k, w) + xColumnModel.addColumn(xCol) + for k, w in kwargs.items(): + setattr(xGridModel, k, w) + self.xDialog.insertByName(name, xGridModel) + return xGridModel + + def run (self, sLang): + self.dUI = enum_strings.getUI(sLang) + + # dialog + self.xDialog = self.xSvMgr.createInstanceWithContext('com.sun.star.awt.UnoControlDialogModel', self.ctx) + self.xDialog.Width = 240 + self.xDialog.Height = 280 + self.xDialog.Title = self.dUI.get('title', "#title#") + xWindowSize = helpers.getWindowSize() + self.xDialog.PositionX = int((xWindowSize.Width / 2) - (self.xDialog.Width / 2)) + self.xDialog.PositionY = int((xWindowSize.Height / 2) - (self.xDialog.Height / 2)) + + # fonts + xFDTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor") + xFDTitle.Height = 9 + xFDTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD") + xFDTitle.Name = "Verdana" + + xFDSubTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor") + xFDSubTitle.Height = 8 + xFDSubTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD") + xFDSubTitle.Name = "Verdana" + + # widget + nX = 10 + nY1 = 5 + nY2 = nY1 + 225 + + nWidth = self.xDialog.Width - 20 + nHeight = 10 + + # List + self._addWidget("list_section", 'FixedLine', nX, nY1, nWidth, nHeight, Label = self.dUI.get("list_section", "#err"), FontDescriptor = xFDTitle) + self._addWidget('count_button', 'Button', nX, nY1+12, 70, 10, Label = self.dUI.get('count_button', "#err")) + self._addWidget('count2_button', 'Button', nX+75, nY1+12, 70, 10, Label = self.dUI.get('count2_button', "#err")) + self._addWidget('unknown_button', 'Button', nX+150, nY1+12, 70, 10, Label = self.dUI.get('unknown_button', "#err")) + self.xGridModel = self._addGrid("list_grid", nX, nY1+25, nWidth, 180, [ + {"Title": self.dUI.get("words", "#err"), "ColumnWidth": 175}, + {"Title": "Occurrences", "ColumnWidth": 45} + ]) + self._addWidget('num_of_entries', 'FixedText', nX, nY1+210, 60, nHeight, Label = self.dUI.get('num_of_entries', "#err"), Align = 2) + self.xNumWord = self._addWidget('num_of_entries_res', 'FixedText', nX+65, nY1+210, 30, nHeight, Label = "—") + self._addWidget('tot_of_entries', 'FixedText', nX+100, nY1+210, 60, nHeight, Label = self.dUI.get('tot_of_entries', "#err"), Align = 2) + self.xTotWord = self._addWidget('tot_of_entries_res', 'FixedText', nX+165, nY1+210, 30, nHeight, Label = "—") + + # Tag + # Note: the only way to group RadioButtons is to create them successively + self._addWidget("dformat_section", 'FixedLine', nX, nY2, 90, nHeight, Label = self.dUI.get("dformat_section", "#err"), FontDescriptor = xFDTitle) + self._addWidget("charstyle_section", 'FixedLine', nX+100, nY2, 90, nHeight, Label = self.dUI.get("charstyle_section", "#err"), FontDescriptor = xFDTitle) + self.xUnderline = self._addWidget('underline', 'RadioButton', nX, nY2+12, 40, nHeight, Label = self.dUI.get('underline', "#err")) + self.xNoUnderline = self._addWidget('nounderline', 'RadioButton', nX+50, nY2+12, 40, nHeight, Label = self.dUI.get('nounderline', "#err")) + self.xAccent = self._addWidget('accentuation', 'RadioButton', nX+100, nY2+12, 50, nHeight, Label = self.dUI.get('accentuation', "#err")) + self.xNoAccent = self._addWidget('noaccentuation', 'RadioButton', nX+155, nY2+12, 40, nHeight, Label = self.dUI.get('noaccentuation', "#err")) + + self.xTag = self._addWidget('tag_button', 'Button', self.xDialog.Width-40, nY2+10, 30, 11, Label = self.dUI.get('tag_button', "#err"), FontDescriptor = xFDTitle, TextColor = 0x005500) + + # Progress bar + self.xProgressBar = self._addWidget('progress_bar', 'ProgressBar', nX, self.xDialog.Height-25, 160, 14) + self.xProgressBar.ProgressValueMin = 0 + self.xProgressBar.ProgressValueMax = 1 # to calculate + + # Close + self._addWidget('close_button', 'Button', self.xDialog.Width-60, self.xDialog.Height-25, 50, 14, Label = self.dUI.get('close_button', "#err"), FontDescriptor = xFDTitle, TextColor = 0x550000) + + # container + self.xContainer = self.xSvMgr.createInstanceWithContext('com.sun.star.awt.UnoControlDialog', self.ctx) + self.xContainer.setModel(self.xDialog) + self.xGridControl = self.xContainer.getControl('list_grid') + self.xContainer.getControl('count_button').addActionListener(self) + self.xContainer.getControl('count_button').setActionCommand('Count') + self.xContainer.getControl('count2_button').addActionListener(self) + self.xContainer.getControl('count2_button').setActionCommand('CountByLemma') + self.xContainer.getControl('unknown_button').addActionListener(self) + self.xContainer.getControl('unknown_button').setActionCommand('UnknownWords') + self.xContainer.getControl('tag_button').addActionListener(self) + self.xContainer.getControl('tag_button').setActionCommand('Tag') + self.xContainer.getControl('close_button').addActionListener(self) + self.xContainer.getControl('close_button').setActionCommand('Close') + self.xContainer.setVisible(False) + xToolkit = self.xSvMgr.createInstanceWithContext('com.sun.star.awt.ExtToolkit', self.ctx) + self.xContainer.createPeer(xToolkit, None) + self.xContainer.execute() + + # XActionListener + def actionPerformed (self, xActionEvent): + try: + if xActionEvent.ActionCommand == "Count": + self.count(self.dUI.get("words", "#err")) + self.xTag.Enabled = True + elif xActionEvent.ActionCommand == "CountByLemma": + self.count(self.dUI.get("lemmas", "#err"), bByLemma=True) + self.xTag.Enabled = False + elif xActionEvent.ActionCommand == "UnknownWords": + self.count(self.dUI.get("unknown_words", "#err"), bOnlyUnknownWords=True) + self.xTag.Enabled = True + elif xActionEvent.ActionCommand == "Tag": + nRow = self.xGridControl.getCurrentRow() + if nRow == -1: + return + sWord = self.xGridModel.GridDataModel.getCellData(0, nRow) + if not sWord: + return + sAction = "" + if self.xUnderline.State: + sAction = "underline" + elif self.xNoUnderline.State: + sAction = "nounderline" + elif self.xAccent.State: + sAction = "accentuation" + elif self.xNoAccent.State: + sAction = "noaccentuation" + self.tagText(sWord, sAction) + elif xActionEvent.ActionCommand == "Close": + self.xContainer.endExecute() + except: + traceback.print_exc() + + # XJobExecutor + def trigger (self, args): + try: + xDialog = Enumerator(self.ctx) + xDialog.run() + except: + traceback.print_exc() + + # Code + def _setTitleOfFirstColumn (self, sTitle): + xColumnModel = self.xGridModel.ColumnModel + xColumn = xColumnModel.getColumn(0) + xColumn.Title = sTitle + + def _getParagraphsFromText (self): + "generator: returns full document text paragraph by paragraph" + xCursor = self.xDocument.Text.createTextCursor() + xCursor.gotoStart(False) + xCursor.gotoEndOfParagraph(True) + yield xCursor.getString() + while xCursor.gotoNextParagraph(False): + xCursor.gotoEndOfParagraph(True) + yield xCursor.getString() + + def _countParagraph (self): + i = 1 + xCursor = self.xDocument.Text.createTextCursor() + xCursor.gotoStart(False) + while xCursor.gotoNextParagraph(False): + i += 1 + return i + + @_waitPointer + def count (self, sTitle, bByLemma=False, bOnlyUnknownWords=False): + if not self.oSpellChecker: + self.oSpellChecker = sc.SpellChecker("fr") + self._setTitleOfFirstColumn(sTitle) + self.xProgressBar.ProgressValueMax = self._countParagraph() * 2 + self.xProgressBar.ProgressValue = 0 + xGridDataModel = self.xGridModel.GridDataModel + xGridDataModel.removeAllRows() + dWord = {} + for sParagraph in self._getParagraphsFromText(): + dWord = self.oSpellChecker.countWordsOccurrences(sParagraph, bByLemma, bOnlyUnknownWords, dWord) + self.xProgressBar.ProgressValue += 1 + self.xProgressBar.ProgressValueMax += len(dWord) + i = 0 + nTotOccur = 0 + for k, w in sorted(dWord.items(), key=lambda t: t[1], reverse=True): + xGridDataModel.addRow(i, (k, w)) + self.xProgressBar.ProgressValue += 1 + i += 1 + nTotOccur += w + self.xProgressBar.ProgressValue = self.xProgressBar.ProgressValueMax + self.xNumWord.Label = str(i) + self.xTotWord.Label = nTotOccur + + @_waitPointer + def tagText (self, sWord, sAction=""): + if not sAction: + return + self.xProgressBar.ProgressValueMax = self._countParagraph() + self.xProgressBar.ProgressValue = 0 + xCursor = self.xDocument.Text.createTextCursor() + #helpers.xray(xCursor) + xCursor.gotoStart(False) + xCursor.gotoEndOfParagraph(True) + sParagraph = xCursor.getString() + if sWord in sParagraph: + self._tagParagraph(sWord, xCursor, sAction) + self.xProgressBar.ProgressValue += 1 + while xCursor.gotoNextParagraph(False): + xCursor.gotoEndOfParagraph(True) + sParagraph = xCursor.getString() + if sWord in sParagraph: + self._tagParagraph(sWord, xCursor, sAction) + self.xProgressBar.ProgressValue += 1 + self.xProgressBar.ProgressValue = self.xProgressBar.ProgressValueMax + + def _tagParagraph (self, sWord, xCursor, sAction): + xCursor.gotoStartOfParagraph(False) + while xCursor.gotoNextWord(False): + if xCursor.isStartOfWord(): + xCursor.gotoEndOfWord(True) + if sWord == xCursor.getString(): + if sAction == "underline": + xCursor.CharBackColor = hexToRBG("AA0000") + elif sAction == "nounderline": + xCursor.CharBackColor = hexToRBG("FFFFFF") + elif sAction == "accentuation": + xCursor.CharStyleName = "Emphasis" + elif sAction == "noaccentuation": + #xCursor.CharStyleName = "Default Style" # doesn’t work + xCursor.setPropertyToDefault("CharStyleName") + + +#g_ImplementationHelper = unohelper.ImplementationHelper() +#g_ImplementationHelper.addImplementation(Enumerator, 'net.grammalecte.enumerator', ('com.sun.star.task.Job',)) ADDED gc_lang/fr/oxt/Lexicographer/enum_strings.py Index: gc_lang/fr/oxt/Lexicographer/enum_strings.py ================================================================== --- gc_lang/fr/oxt/Lexicographer/enum_strings.py +++ gc_lang/fr/oxt/Lexicographer/enum_strings.py @@ -0,0 +1,55 @@ +def getUI (sLang): + if sLang in dStrings: + return dStrings[sLang] + return dStrings["fr"] + +dStrings = { + "fr": { + "title": "Grammalecte · Recenseur de mots", + + "list_section": "Calcul des occurrences des mots", + "count_button": "Compter tout", + "count2_button": "Compter par lemme", + "unknown_button": "Mots inconnus", + "num_of_entries": "Nombre d’entrées :", + "tot_of_entries": "Total des entrées :", + + "words": "Mots", + "lemmas": "Lemmes", + "unknown_words": "Mots inconnus", + + "dformat_section": "Formatage direct", + "charstyle_section": "Style de caractères", + "underline": "Surligner", + "nounderline": "Effacer", + "accentuation": "Accentuation", + "noaccentuation": "Aucun", + "tag_button": "Taguer", + + "close_button": "Fermer", + }, + "en": { + "title": "Grammalecte · Enumerator of Words", + + "list_section": "Words", + "count_button": "Count all", + "count2_button": "Count by lemma", + "unknown_button": "Unknown words", + "num_of_entries": "Number of entries:", + "tot_of_entries": "Total of words:", + + "words": "Words", + "lemmas": "Lemmas", + "unknown_words": "Unknown words", + + "dformat_section": "Direct format", + "charstyle_section": "Character style", + "underline": "Underline", + "nounderline": "Erase", + "accentuation": "Accentuation", + "noaccentuation": "None", + "tag_button": "Tag", + + "close_button": "Close", + }, +} Index: gc_lang/fr/oxt/addons.xcu ================================================================== --- gc_lang/fr/oxt/addons.xcu +++ gc_lang/fr/oxt/addons.xcu @@ -69,10 +69,26 @@ com.sun.star.text.TextDocument,com.sun.star.text.GlobalDocument,com.sun.star.text.WebDocument,com.sun.star.presentation.PresentationDocument + + service:net.grammalecte.AppLauncher?EN + + + + ~Recenseur de mots… + ~Enumerator of words… + + + _self + + + com.sun.star.text.TextDocument,com.sun.star.text.GlobalDocument,com.sun.star.text.WebDocument,com.sun.star.presentation.PresentationDocument + + + service:net.grammalecte.AppLauncher?MA @@ -87,19 +103,19 @@ com.sun.star.text.TextDocument,com.sun.star.text.GlobalDocument,com.sun.star.text.WebDocument - + private:separator com.sun.star.text.TextDocument,com.sun.star.text.GlobalDocument,com.sun.star.text.WebDocument,com.sun.star.presentation.PresentationDocument - + service:net.grammalecte.AppLauncher?OP