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