1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Linguist of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "phrase.h"
30 #include "translator.h"
31 #include "xmlparser.h"
32
33 #include <QApplication>
34 #include <QFile>
35 #include <QFileInfo>
36 #include <QMessageBox>
37 #include <QRegExp>
38 #include <QTextCodec>
39 #include <QTextStream>
40 #include <QXmlStreamReader>
41
42 QT_BEGIN_NAMESPACE
43
protect(const QString & str)44 static QString protect(const QString & str)
45 {
46 QString p = str;
47 p.replace(QLatin1Char('&'), QLatin1String("&"));
48 p.replace(QLatin1Char('\"'), QLatin1String("""));
49 p.replace(QLatin1Char('>'), QLatin1String(">"));
50 p.replace(QLatin1Char('<'), QLatin1String("<"));
51 p.replace(QLatin1Char('\''), QLatin1String("'"));
52 return p;
53 }
54
Phrase()55 Phrase::Phrase()
56 : shrtc(-1), m_phraseBook(0)
57 {
58 }
59
Phrase(const QString & source,const QString & target,const QString & definition,const Candidate & candidate,int sc)60 Phrase::Phrase(const QString &source, const QString &target, const QString &definition,
61 const Candidate &candidate, int sc)
62 : shrtc(sc), s(source), t(target), d(definition), cand(candidate), m_phraseBook(0)
63 {
64 }
65
Phrase(const QString & source,const QString & target,const QString & definition,PhraseBook * phraseBook)66 Phrase::Phrase(const QString &source, const QString &target,
67 const QString &definition, PhraseBook *phraseBook)
68 : shrtc(-1), s(source), t(target), d(definition),
69 m_phraseBook(phraseBook)
70 {
71 }
72
setSource(const QString & ns)73 void Phrase::setSource(const QString &ns)
74 {
75 if (s == ns)
76 return;
77 s = ns;
78 if (m_phraseBook)
79 m_phraseBook->phraseChanged(this);
80 }
81
setTarget(const QString & nt)82 void Phrase::setTarget(const QString &nt)
83 {
84 if (t == nt)
85 return;
86 t = nt;
87 if (m_phraseBook)
88 m_phraseBook->phraseChanged(this);
89 }
90
setDefinition(const QString & nd)91 void Phrase::setDefinition(const QString &nd)
92 {
93 if (d == nd)
94 return;
95 d = nd;
96 if (m_phraseBook)
97 m_phraseBook->phraseChanged(this);
98 }
99
operator ==(const Phrase & p,const Phrase & q)100 bool operator==(const Phrase &p, const Phrase &q)
101 {
102 return p.source() == q.source() && p.target() == q.target() &&
103 p.definition() == q.definition() && p.phraseBook() == q.phraseBook();
104 }
105
106 class QphHandler : public XmlParser
107 {
108 public:
QphHandler(PhraseBook * phraseBook,QXmlStreamReader & reader)109 QphHandler(PhraseBook *phraseBook, QXmlStreamReader &reader)
110 : XmlParser(reader), pb(phraseBook), ferrorCount(0)
111 {
112 }
113 ~QphHandler() override = default;
114
language() const115 QString language() const { return m_language; }
sourceLanguage() const116 QString sourceLanguage() const { return m_sourceLanguage; }
117
118 private:
119 bool startElement(const QStringRef &namespaceURI, const QStringRef &localName,
120 const QStringRef &qName, const QXmlStreamAttributes &atts) override;
121 bool endElement(const QStringRef &namespaceURI, const QStringRef &localName,
122 const QStringRef &qName) override;
123 bool characters(const QStringRef &ch) override;
124 bool fatalError(qint64 line, qint64 column, const QString &message) override;
125
126 PhraseBook *pb;
127 QString source;
128 QString target;
129 QString definition;
130 QString m_language;
131 QString m_sourceLanguage;
132
133 QString accum;
134 int ferrorCount;
135 };
136
startElement(const QStringRef & namespaceURI,const QStringRef & localName,const QStringRef & qName,const QXmlStreamAttributes & atts)137 bool QphHandler::startElement(const QStringRef &namespaceURI, const QStringRef &localName,
138 const QStringRef &qName, const QXmlStreamAttributes &atts)
139 {
140 Q_UNUSED(namespaceURI)
141 Q_UNUSED(localName)
142
143 if (qName == QLatin1String("QPH")) {
144 m_language = atts.value(QLatin1String("language")).toString();
145 m_sourceLanguage = atts.value(QLatin1String("sourcelanguage")).toString();
146 } else if (qName == QLatin1String("phrase")) {
147 source.truncate(0);
148 target.truncate(0);
149 definition.truncate(0);
150 }
151 accum.truncate(0);
152 return true;
153 }
154
endElement(const QStringRef & namespaceURI,const QStringRef & localName,const QStringRef & qName)155 bool QphHandler::endElement(const QStringRef &namespaceURI, const QStringRef &localName,
156 const QStringRef &qName)
157 {
158 Q_UNUSED(namespaceURI)
159 Q_UNUSED(localName)
160
161 if (qName == QLatin1String("source"))
162 source = accum;
163 else if (qName == QLatin1String("target"))
164 target = accum;
165 else if (qName == QLatin1String("definition"))
166 definition = accum;
167 else if (qName == QLatin1String("phrase"))
168 pb->m_phrases.append(new Phrase(source, target, definition, pb));
169 return true;
170 }
171
characters(const QStringRef & ch)172 bool QphHandler::characters(const QStringRef &ch)
173 {
174 accum += ch;
175 return true;
176 }
177
fatalError(qint64 line,qint64 column,const QString & message)178 bool QphHandler::fatalError(qint64 line, qint64 column, const QString &message)
179 {
180 if (ferrorCount++ == 0) {
181 QString msg = PhraseBook::tr("Parse error at line %1, column %2 (%3).")
182 .arg(line)
183 .arg(column)
184 .arg(message);
185 QMessageBox::information(nullptr, QObject::tr("Qt Linguist"), msg);
186 }
187 return false;
188 }
189
PhraseBook()190 PhraseBook::PhraseBook() :
191 m_changed(false),
192 m_language(QLocale::C),
193 m_sourceLanguage(QLocale::C),
194 m_country(QLocale::AnyCountry),
195 m_sourceCountry(QLocale::AnyCountry)
196 {
197 }
198
~PhraseBook()199 PhraseBook::~PhraseBook()
200 {
201 qDeleteAll(m_phrases);
202 }
203
setLanguageAndCountry(QLocale::Language lang,QLocale::Country country)204 void PhraseBook::setLanguageAndCountry(QLocale::Language lang, QLocale::Country country)
205 {
206 if (m_language == lang && m_country == country)
207 return;
208 m_language = lang;
209 m_country = country;
210 setModified(true);
211 }
212
setSourceLanguageAndCountry(QLocale::Language lang,QLocale::Country country)213 void PhraseBook::setSourceLanguageAndCountry(QLocale::Language lang, QLocale::Country country)
214 {
215 if (m_sourceLanguage == lang && m_sourceCountry == country)
216 return;
217 m_sourceLanguage = lang;
218 m_sourceCountry = country;
219 setModified(true);
220 }
221
load(const QString & fileName,bool * langGuessed)222 bool PhraseBook::load(const QString &fileName, bool *langGuessed)
223 {
224 QFile f(fileName);
225 if (!f.open(QIODevice::ReadOnly))
226 return false;
227
228 m_fileName = fileName;
229
230 QXmlStreamReader reader(&f);
231 QphHandler *hand = new QphHandler(this, reader);
232 reader.setNamespaceProcessing(false);
233 bool ok = hand->parse();
234
235 Translator::languageAndCountry(hand->language(), &m_language, &m_country);
236 *langGuessed = false;
237 if (m_language == QLocale::C) {
238 QLocale sys;
239 m_language = sys.language();
240 m_country = sys.country();
241 *langGuessed = true;
242 }
243
244 QString lang = hand->sourceLanguage();
245 if (lang.isEmpty()) {
246 m_sourceLanguage = QLocale::C;
247 m_sourceCountry = QLocale::AnyCountry;
248 } else {
249 Translator::languageAndCountry(lang, &m_sourceLanguage, &m_sourceCountry);
250 }
251
252 delete hand;
253 f.close();
254 if (!ok) {
255 qDeleteAll(m_phrases);
256 m_phrases.clear();
257 } else {
258 emit listChanged();
259 }
260
261 return ok;
262 }
263
save(const QString & fileName)264 bool PhraseBook::save(const QString &fileName)
265 {
266 QFile f(fileName);
267 if (!f.open(QIODevice::WriteOnly))
268 return false;
269
270 m_fileName = fileName;
271
272 QTextStream t(&f);
273 t.setCodec( QTextCodec::codecForName("UTF-8") );
274
275 t << "<!DOCTYPE QPH>\n<QPH";
276 if (sourceLanguage() != QLocale::C)
277 t << " sourcelanguage=\""
278 << Translator::makeLanguageCode(sourceLanguage(), sourceCountry()) << '"';
279 if (language() != QLocale::C)
280 t << " language=\"" << Translator::makeLanguageCode(language(), country()) << '"';
281 t << ">\n";
282 foreach (Phrase *p, m_phrases) {
283 t << "<phrase>\n";
284 t << " <source>" << protect( p->source() ) << "</source>\n";
285 t << " <target>" << protect( p->target() ) << "</target>\n";
286 if (!p->definition().isEmpty())
287 t << " <definition>" << protect( p->definition() )
288 << "</definition>\n";
289 t << "</phrase>\n";
290 }
291 t << "</QPH>\n";
292 f.close();
293 setModified(false);
294 return true;
295 }
296
append(Phrase * phrase)297 void PhraseBook::append(Phrase *phrase)
298 {
299 m_phrases.append(phrase);
300 phrase->setPhraseBook(this);
301 setModified(true);
302 emit listChanged();
303 }
304
remove(Phrase * phrase)305 void PhraseBook::remove(Phrase *phrase)
306 {
307 m_phrases.removeOne(phrase);
308 phrase->setPhraseBook(0);
309 setModified(true);
310 emit listChanged();
311 }
312
setModified(bool modified)313 void PhraseBook::setModified(bool modified)
314 {
315 if (m_changed != modified) {
316 emit modifiedChanged(modified);
317 m_changed = modified;
318 }
319 }
320
phraseChanged(Phrase * p)321 void PhraseBook::phraseChanged(Phrase *p)
322 {
323 Q_UNUSED(p);
324
325 setModified(true);
326 }
327
friendlyPhraseBookName() const328 QString PhraseBook::friendlyPhraseBookName() const
329 {
330 if (!m_fileName.isEmpty())
331 return QFileInfo(m_fileName).fileName();
332 return QString();
333 }
334
335 QT_END_NAMESPACE
336