1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include "hunspellpluginimpl.h"
8 #include "hunspelldialog.h"
9 #include "langmgr.h"
10 #include "pageitem.h"
11 #include "pageitem_textframe.h"
12 #include "scpaths.h"
13 #include "scribus.h"
14 #include "scribusdoc.h"
15 #include "scribusview.h"
16 #include "selection.h"
17 #include "text/scworditerator.h"
18 #include "text/specialchars.h"
19 #include "ui/storyeditor.h"
20 #include "util.h"
21
22
23 #include <QDebug>
24 #include <QDir>
25 #include <QFile>
26 #include <QMessageBox>
27
28 #ifdef Q_OS_WIN32
29 #include <windows.h>
30 #include <shlobj.h>
31 #endif
32
33
34 // Initialize members here, if any
HunspellPluginImpl()35 HunspellPluginImpl::HunspellPluginImpl() : QObject(nullptr)
36 {
37 m_doc = nullptr;
38 m_runningForSE = false;
39 m_SE = nullptr;
40 }
41
~HunspellPluginImpl()42 HunspellPluginImpl::~HunspellPluginImpl()
43 {
44 foreach (HunspellDict* h, hspellerMap)
45 {
46 delete h;
47 h = nullptr;
48 }
49 hspellerMap.clear();
50 }
51
run(const QString & target,ScribusDoc * doc)52 bool HunspellPluginImpl::run(const QString & target, ScribusDoc* doc)
53 {
54 m_doc = doc;
55 bool initOk = initHunspell();
56 if (!initOk)
57 return false;
58 bool spellCheckOk = false;
59 if (m_runningForSE)
60 spellCheckOk = checkWithHunspellSE();
61 else
62 spellCheckOk = checkWithHunspell();
63 return spellCheckOk;
64 }
65
initHunspell()66 bool HunspellPluginImpl::initHunspell()
67 {
68 bool dictPathFound = LanguageManager::instance()->findSpellingDictionaries(dictionaryPaths);
69 if (!dictPathFound)
70 {
71 qDebug()<<"No preinstalled dictonary paths found";
72 return false;
73 }
74 dictionaryMap.clear();
75 LanguageManager::instance()->findSpellingDictionarySets(dictionaryPaths, dictionaryMap);
76 if (dictionaryMap.count() == 0)
77 return false;
78
79 //Initialise one hunspeller for each dictionary found
80 auto it = dictionaryMap.cbegin();
81 while (it != dictionaryMap.cend())
82 {
83 //qDebug() << "hunspell init:" << it.key() << it.value();
84 hspellerMap.insert(it.key(), new HunspellDict(it.value() + ".aff", it.value() + ".dic"));
85 ++it;
86 }
87 return true;
88 }
89
checkWithHunspell()90 bool HunspellPluginImpl::checkWithHunspell()
91 {
92 PageItem *frameToCheck;
93
94 for (int i = 0; i < m_doc->m_Selection->count(); ++i)
95 {
96 frameToCheck = m_doc->m_Selection->itemAt(i);
97 StoryText *iText = &frameToCheck->itemText;
98 parseTextFrame(iText);
99 openGUIForTextFrame(frameToCheck);
100 m_doc->view()->DrawNew();
101 }
102 return true;
103 }
104
checkWithHunspellSE()105 bool HunspellPluginImpl::checkWithHunspellSE()
106 {
107 StoryText *iText=&(m_SE->Editor->StyledText);
108 parseTextFrame(iText);
109 openGUIForStoryEditor(iText);
110 m_SE->Editor->updateAll();
111 return true;
112 }
113
parseTextFrame(StoryText * iText)114 bool HunspellPluginImpl::parseTextFrame(StoryText *iText)
115 {
116 ScWordIterator wordIt(*iText);
117
118 int len = iText->length();
119 int currPos = wordIt.firstWord();
120 int wordStart;
121 while (currPos < len)
122 {
123 wordStart = currPos;
124 int wordEnd = wordIt.endOfWord(wordStart);
125 QString word = iText->text(wordStart, wordEnd - wordStart);
126 // remove any Ignorable Code Point
127 QString tmp = word;
128 QString tmp2;
129 for (int i = 0; i < word.size(); ++i)
130 {
131 if (!SpecialChars::isIgnorableCodePoint(tmp.at(i).unicode()))
132 tmp2 += tmp.at(i);
133 }
134 word = tmp2;
135
136 QString wordLang = iText->charStyle(wordStart).language();
137 if (wordLang.isEmpty())
138 {
139 const StyleSet<CharStyle> &tmp(m_doc->charStyles());
140 const CharStyle* defaultStyle = tmp.getDefault();
141 if (defaultStyle)
142 wordLang = defaultStyle->language();
143 }
144 //we now use the abbreviation
145 //wordLang = LanguageManager::instance()->getAbbrevFromLang(wordLang, true, false);
146 //A little hack as for some reason our en dictionary from the aspell plugin was not called en_GB or en_US but en, content was en_GB though. Meh.
147 if (wordLang == "en")
148 wordLang = "en_GB";
149 //qDebug() << "Word:" << word << wordLang;
150 if (!dictionaryMap.contains(wordLang))
151 {
152 //qDebug() << "Spelling language to match style language NOT installed (" << wordLang << ")";
153 QString altLang = LanguageManager::instance()->getAlternativeAbbrevfromAbbrev(wordLang);
154 if (!altLang.isEmpty())
155 {
156 //qDebug() << "altLang" << altLang << dictionaryMap.contains(altLang);
157 wordLang = altLang;
158 }
159 }
160 else
161 {
162 //qDebug() << "Spelling language to match style language IS installed (" << wordLang << ")";
163 int i = 0;
164 auto it = dictionaryMap.cbegin();
165 while (it != dictionaryMap.cend())
166 {
167 if (it.key() == wordLang)
168 break;
169 ++i;
170 ++it;
171 }
172 }
173
174 if (hspellerMap.contains(wordLang) && hspellerMap[wordLang]->spell(word)==0)
175 {
176 //qDebug() << "hspellerMap.contains(wordLang)" << hspellerMap.contains(wordLang) << "hspellerMap[wordLang]->spell(word)" << hspellerMap[wordLang]->spell(word);
177 struct WordsFound wf;
178 wf.start = wordStart;
179 wf.end = wordEnd;
180 wf.w = word;
181 wf.changed = false;
182 wf.ignore = false;
183 wf.changeOffset = 0;
184 wf.lang = wordLang;
185 wordsToCorrect.append(wf);
186 }
187 currPos = wordIt.nextWord(wordStart);
188 }
189 return true;
190 }
191
openGUIForTextFrame(PageItem * item)192 bool HunspellPluginImpl::openGUIForTextFrame(PageItem *item)
193 {
194 HunspellDialog hsDialog(m_doc->scMW(), m_doc, item);
195 hsDialog.set(&dictionaryMap, &hspellerMap, &wordsToCorrect);
196 hsDialog.exec();
197 if (hsDialog.docChanged())
198 m_doc->changed();
199 return true;
200 }
201
openGUIForStoryEditor(StoryText * iText)202 bool HunspellPluginImpl::openGUIForStoryEditor(StoryText *iText)
203 {
204 m_SE->setSpellActive(true);
205 HunspellDialog hsDialog(m_SE, m_doc, iText);
206 hsDialog.set(&dictionaryMap, &hspellerMap, &wordsToCorrect);
207 hsDialog.exec();
208 m_SE->setSpellActive(false);
209 return true;
210 }
211
setRunningForSE(bool rfSE,StoryEditor * sE)212 void HunspellPluginImpl::setRunningForSE(bool rfSE, StoryEditor *sE)
213 {
214 m_runningForSE = rfSE;
215 m_SE = sE;
216 }
217
218