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
42QT_BEGIN_NAMESPACE
43
44static QString protect(const QString & str)
45{
46 QString p = str;
47 p.replace(c: QLatin1Char('&'), after: QLatin1String("&amp;"));
48 p.replace(c: QLatin1Char('\"'), after: QLatin1String("&quot;"));
49 p.replace(c: QLatin1Char('>'), after: QLatin1String("&gt;"));
50 p.replace(c: QLatin1Char('<'), after: QLatin1String("&lt;"));
51 p.replace(c: QLatin1Char('\''), after: QLatin1String("&apos;"));
52 return p;
53}
54
55Phrase::Phrase()
56 : shrtc(-1), m_phraseBook(0)
57{
58}
59
60Phrase::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
66Phrase::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
73void Phrase::setSource(const QString &ns)
74{
75 if (s == ns)
76 return;
77 s = ns;
78 if (m_phraseBook)
79 m_phraseBook->phraseChanged(phrase: this);
80}
81
82void Phrase::setTarget(const QString &nt)
83{
84 if (t == nt)
85 return;
86 t = nt;
87 if (m_phraseBook)
88 m_phraseBook->phraseChanged(phrase: this);
89}
90
91void Phrase::setDefinition(const QString &nd)
92{
93 if (d == nd)
94 return;
95 d = nd;
96 if (m_phraseBook)
97 m_phraseBook->phraseChanged(phrase: this);
98}
99
100bool 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
106class QphHandler : public XmlParser
107{
108public:
109 QphHandler(PhraseBook *phraseBook, QXmlStreamReader &reader)
110 : XmlParser(reader), pb(phraseBook), ferrorCount(0)
111 {
112 }
113 ~QphHandler() override = default;
114
115 QString language() const { return m_language; }
116 QString sourceLanguage() const { return m_sourceLanguage; }
117
118private:
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
137bool 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(qualifiedName: QLatin1String("language")).toString();
145 m_sourceLanguage = atts.value(qualifiedName: QLatin1String("sourcelanguage")).toString();
146 } else if (qName == QLatin1String("phrase")) {
147 source.truncate(pos: 0);
148 target.truncate(pos: 0);
149 definition.truncate(pos: 0);
150 }
151 accum.truncate(pos: 0);
152 return true;
153}
154
155bool 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(t: new Phrase(source, target, definition, pb));
169 return true;
170}
171
172bool QphHandler::characters(const QStringRef &ch)
173{
174 accum += ch;
175 return true;
176}
177
178bool QphHandler::fatalError(qint64 line, qint64 column, const QString &message)
179{
180 if (ferrorCount++ == 0) {
181 QString msg = PhraseBook::tr(s: "Parse error at line %1, column %2 (%3).")
182 .arg(a: line)
183 .arg(a: column)
184 .arg(a: message);
185 QMessageBox::information(parent: nullptr, title: QObject::tr(s: "Qt Linguist"), text: msg);
186 }
187 return false;
188}
189
190PhraseBook::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
199PhraseBook::~PhraseBook()
200{
201 qDeleteAll(c: m_phrases);
202}
203
204void 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
213void 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
222bool PhraseBook::load(const QString &fileName, bool *langGuessed)
223{
224 QFile f(fileName);
225 if (!f.open(flags: 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(languageCode: hand->language(), lang: &m_language, country: &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(languageCode: lang, lang: &m_sourceLanguage, country: &m_sourceCountry);
250 }
251
252 delete hand;
253 f.close();
254 if (!ok) {
255 qDeleteAll(c: m_phrases);
256 m_phrases.clear();
257 } else {
258 emit listChanged();
259 }
260
261 return ok;
262}
263
264bool PhraseBook::save(const QString &fileName)
265{
266 QFile f(fileName);
267 if (!f.open(flags: QIODevice::WriteOnly))
268 return false;
269
270 m_fileName = fileName;
271
272 QTextStream t(&f);
273 t.setCodec( QTextCodec::codecForName(name: "UTF-8") );
274
275 t << "<!DOCTYPE QPH>\n<QPH";
276 if (sourceLanguage() != QLocale::C)
277 t << " sourcelanguage=\""
278 << Translator::makeLanguageCode(language: sourceLanguage(), country: sourceCountry()) << '"';
279 if (language() != QLocale::C)
280 t << " language=\"" << Translator::makeLanguageCode(language: language(), country: country()) << '"';
281 t << ">\n";
282 foreach (Phrase *p, m_phrases) {
283 t << "<phrase>\n";
284 t << " <source>" << protect( str: p->source() ) << "</source>\n";
285 t << " <target>" << protect( str: p->target() ) << "</target>\n";
286 if (!p->definition().isEmpty())
287 t << " <definition>" << protect( str: 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
297void PhraseBook::append(Phrase *phrase)
298{
299 m_phrases.append(t: phrase);
300 phrase->setPhraseBook(this);
301 setModified(true);
302 emit listChanged();
303}
304
305void PhraseBook::remove(Phrase *phrase)
306{
307 m_phrases.removeOne(t: phrase);
308 phrase->setPhraseBook(0);
309 setModified(true);
310 emit listChanged();
311}
312
313void PhraseBook::setModified(bool modified)
314 {
315 if (m_changed != modified) {
316 emit modifiedChanged(changed: modified);
317 m_changed = modified;
318 }
319}
320
321void PhraseBook::phraseChanged(Phrase *p)
322{
323 Q_UNUSED(p);
324
325 setModified(true);
326}
327
328QString PhraseBook::friendlyPhraseBookName() const
329{
330 if (!m_fileName.isEmpty())
331 return QFileInfo(m_fileName).fileName();
332 return QString();
333}
334
335QT_END_NAMESPACE
336

source code of qttools/src/linguist/linguist/phrase.cpp