1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#ifndef MESSAGEMODEL_H
5#define MESSAGEMODEL_H
6
7#include "translator.h"
8
9#include <QtCore/QAbstractItemModel>
10#include <QtCore/QList>
11#include <QtCore/QHash>
12#include <QtCore/QLocale>
13#include <QtGui/QColor>
14#include <QtGui/QBitmap>
15
16QT_BEGIN_NAMESPACE
17
18class DataModel;
19class MultiDataModel;
20struct StatisticalData;
21
22class MessageItem
23{
24public:
25 MessageItem(const TranslatorMessage &message);
26
27 bool danger() const { return m_danger; }
28 void setDanger(bool danger) { m_danger = danger; }
29
30 void setTranslation(const QString &translation)
31 { m_message.setTranslation(translation); }
32
33 QString id() const { return m_message.id(); }
34 QString context() const { return m_message.context(); }
35 QString text() const { return m_message.sourceText(); }
36 QString pluralText() const { return m_message.extra(ba: QLatin1String("po-msgid_plural")); }
37 QString comment() const { return m_message.comment(); }
38 QString fileName() const { return m_message.fileName(); }
39 QString extraComment() const { return m_message.extraComment(); }
40 QString translatorComment() const { return m_message.translatorComment(); }
41 void setTranslatorComment(const QString &cmt) { m_message.setTranslatorComment(cmt); }
42 int lineNumber() const { return m_message.lineNumber(); }
43 QString translation() const { return m_message.translation(); }
44 QStringList translations() const { return m_message.translations(); }
45 void setTranslations(const QStringList &translations)
46 { m_message.setTranslations(translations); }
47
48 TranslatorMessage::Type type() const { return m_message.type(); }
49 void setType(TranslatorMessage::Type type) { m_message.setType(type); }
50
51 bool isFinished() const { return type() == TranslatorMessage::Finished; }
52 bool isUnfinished() const { return type() == TranslatorMessage::Unfinished; }
53 bool isObsolete() const
54 { return type() == TranslatorMessage::Obsolete || type() == TranslatorMessage::Vanished; }
55 const TranslatorMessage &message() const { return m_message; }
56
57 bool compare(const QString &findText, bool matchSubstring,
58 Qt::CaseSensitivity cs) const;
59
60private:
61 TranslatorMessage m_message;
62 bool m_danger;
63};
64
65
66class ContextItem
67{
68public:
69 ContextItem(const QString &context);
70
71 int finishedDangerCount() const { return m_finishedDangerCount; }
72 int unfinishedDangerCount() const { return m_unfinishedDangerCount; }
73
74 int finishedCount() const { return m_finishedCount; }
75 int unfinishedCount() const { return m_nonobsoleteCount - m_finishedCount; }
76 int nonobsoleteCount() const { return m_nonobsoleteCount; }
77
78 QString context() const { return m_context; }
79 QString comment() const { return m_comment; }
80 QString fullContext() const { return m_comment.trimmed(); }
81
82 // For item status in context list
83 bool isObsolete() const { return !nonobsoleteCount(); }
84 bool isFinished() const { return unfinishedCount() == 0; }
85
86 MessageItem *messageItem(int i) const;
87 int messageCount() const { return msgItemList.size(); }
88
89 MessageItem *findMessage(const QString &sourcetext, const QString &comment) const;
90
91private:
92 friend class DataModel;
93 friend class MultiDataModel;
94 void appendMessage(const MessageItem &msg) { msgItemList.append(t: msg); }
95 void appendToComment(const QString &x);
96 void incrementFinishedCount() { ++m_finishedCount; }
97 void decrementFinishedCount() { --m_finishedCount; }
98 void incrementFinishedDangerCount() { ++m_finishedDangerCount; }
99 void decrementFinishedDangerCount() { --m_finishedDangerCount; }
100 void incrementUnfinishedDangerCount() { ++m_unfinishedDangerCount; }
101 void decrementUnfinishedDangerCount() { --m_unfinishedDangerCount; }
102 void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
103
104 QString m_comment;
105 QString m_context;
106 int m_finishedCount;
107 int m_finishedDangerCount;
108 int m_unfinishedDangerCount;
109 int m_nonobsoleteCount;
110 QList<MessageItem> msgItemList;
111};
112
113
114class DataIndex
115{
116public:
117 DataIndex() : m_context(-1), m_message(-1) {}
118 DataIndex(int context, int message) : m_context(context), m_message(message) {}
119 int context() const { return m_context; }
120 int message() const { return m_message; }
121 bool isValid() const { return m_context >= 0; }
122protected:
123 int m_context;
124 int m_message;
125};
126
127
128class DataModelIterator : public DataIndex
129{
130public:
131 DataModelIterator(DataModel *model, int contextNo = 0, int messageNo = 0);
132 MessageItem *current() const;
133 bool isValid() const;
134 void operator++();
135private:
136 DataModelIterator() {}
137 DataModel *m_model; // not owned
138};
139
140
141class DataModel : public QObject
142{
143 Q_OBJECT
144public:
145 DataModel(QObject *parent = 0);
146
147 enum FindLocation { NoLocation = 0, SourceText = 0x1, Translations = 0x2, Comments = 0x4 };
148
149 // Specializations
150 int contextCount() const { return m_contextList.size(); }
151 ContextItem *findContext(const QString &context) const;
152 MessageItem *findMessage(const QString &context, const QString &sourcetext,
153 const QString &comment) const;
154
155 ContextItem *contextItem(int index) const;
156 MessageItem *messageItem(const DataIndex &index) const;
157
158 int messageCount() const { return m_numMessages; }
159 bool isEmpty() const { return m_numMessages == 0; }
160 bool isModified() const { return m_modified; }
161 void setModified(bool dirty);
162 bool isWritable() const { return m_writable; }
163 void setWritable(bool writable) { m_writable = writable; }
164
165 bool isWellMergeable(const DataModel *other) const;
166 bool load(const QString &fileName, bool *langGuessed, QWidget *parent);
167 bool save(QWidget *parent) { return save(fileName: m_srcFileName, parent); }
168 bool saveAs(const QString &newFileName, QWidget *parent);
169 bool release(const QString &fileName, bool verbose,
170 bool ignoreUnfinished, TranslatorSaveMode mode, QWidget *parent);
171 QString srcFileName(bool pretty = false) const
172 { return pretty ? prettifyPlainFileName(fn: m_srcFileName) : m_srcFileName; }
173
174 static QString prettifyPlainFileName(const QString &fn);
175 static QString prettifyFileName(const QString &fn);
176
177 bool setLanguageAndTerritory(QLocale::Language lang, QLocale::Territory territory);
178 QLocale::Language language() const { return m_language; }
179 QLocale::Territory territory() const { return m_territory; }
180 void setSourceLanguageAndTerritory(QLocale::Language lang, QLocale::Territory territory);
181 QLocale::Language sourceLanguage() const { return m_sourceLanguage; }
182 QLocale::Territory sourceTerritory() const { return m_sourceTerritory; }
183
184 const QString &localizedLanguage() const { return m_localizedLanguage; }
185 const QStringList &numerusForms() const { return m_numerusForms; }
186 const QList<bool> &countRefNeeds() const { return m_countRefNeeds; }
187
188 QStringList normalizedTranslations(const MessageItem &m) const;
189 void doCharCounting(const QString& text, int& trW, int& trC, int& trCS);
190 void updateStatistics();
191
192 int getSrcWords() const { return m_srcWords; }
193 int getSrcChars() const { return m_srcChars; }
194 int getSrcCharsSpc() const { return m_srcCharsSpc; }
195
196signals:
197 void statsChanged(const StatisticalData &newStats);
198 void progressChanged(int finishedCount, int oldFinishedCount);
199 void languageChanged();
200 void modifiedChanged();
201
202private:
203 friend class DataModelIterator;
204 QList<ContextItem> m_contextList;
205
206 bool save(const QString &fileName, QWidget *parent);
207 void updateLocale();
208
209 bool m_writable;
210 bool m_modified;
211
212 int m_numMessages;
213
214 // For statistics
215 int m_srcWords;
216 int m_srcChars;
217 int m_srcCharsSpc;
218
219 QString m_srcFileName;
220 QLocale::Language m_language;
221 QLocale::Language m_sourceLanguage;
222 QLocale::Territory m_territory;
223 QLocale::Territory m_sourceTerritory;
224 bool m_relativeLocations;
225 Translator::ExtraData m_extra;
226
227 QString m_localizedLanguage;
228 QStringList m_numerusForms;
229 QList<bool> m_countRefNeeds;
230};
231
232
233struct MultiMessageItem
234{
235public:
236 MultiMessageItem(const MessageItem *m);
237 QString id() const { return m_id; }
238 QString text() const { return m_text; }
239 QString pluralText() const { return m_pluralText; }
240 QString comment() const { return m_comment; }
241 bool isEmpty() const { return !m_nonnullCount; }
242 // The next two include also read-only
243 bool isObsolete() const { return m_nonnullCount && !m_nonobsoleteCount; }
244 int countNonobsolete() const { return m_nonobsoleteCount; }
245 // The next three include only read-write
246 int countEditable() const { return m_editableCount; }
247 bool isUnfinished() const { return m_unfinishedCount != 0; }
248 int countUnfinished() const { return m_unfinishedCount; }
249
250private:
251 friend class MultiDataModel;
252 void incrementNonnullCount() { ++m_nonnullCount; }
253 void decrementNonnullCount() { --m_nonnullCount; }
254 void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
255 void decrementNonobsoleteCount() { --m_nonobsoleteCount; }
256 void incrementEditableCount() { ++m_editableCount; }
257 void decrementEditableCount() { --m_editableCount; }
258 void incrementUnfinishedCount() { ++m_unfinishedCount; }
259 void decrementUnfinishedCount() { --m_unfinishedCount; }
260
261 QString m_id;
262 QString m_text;
263 QString m_pluralText;
264 QString m_comment;
265 int m_nonnullCount; // all
266 int m_nonobsoleteCount; // all
267 int m_editableCount; // read-write
268 int m_unfinishedCount; // read-write
269};
270
271struct MultiContextItem
272{
273public:
274 MultiContextItem(int oldCount, ContextItem *ctx, bool writable);
275
276 ContextItem *contextItem(int model) const { return m_contextList[model]; }
277
278 MultiMessageItem *multiMessageItem(int msgIdx) const
279 { return const_cast<MultiMessageItem *>(&m_multiMessageList[msgIdx]); }
280 MessageItem *messageItem(int model, int msgIdx) const { return m_messageLists[model][msgIdx]; }
281 int firstNonobsoleteMessageIndex(int msgIdx) const;
282 int findMessage(const QString &sourcetext, const QString &comment) const;
283 int findMessageById(const QString &id) const;
284
285 QString context() const { return m_context; }
286 QString comment() const { return m_comment; }
287 int messageCount() const { return m_messageLists.isEmpty() ? 0 : m_messageLists[0].size(); }
288 // For item count in context list
289 int getNumFinished() const { return m_finishedCount; }
290 int getNumEditable() const { return m_editableCount; }
291 // For background in context list
292 bool isObsolete() const { return messageCount() && !m_nonobsoleteCount; }
293
294private:
295 friend class MultiDataModel;
296 void appendEmptyModel();
297 void assignLastModel(ContextItem *ctx, bool writable);
298 void removeModel(int pos);
299 void moveModel(int oldPos, int newPos); // newPos is *before* removing at oldPos
300 void putMessageItem(int pos, MessageItem *m);
301 void appendMessageItems(const QList<MessageItem *> &m);
302 void removeMultiMessageItem(int pos);
303 void incrementFinishedCount() { ++m_finishedCount; }
304 void decrementFinishedCount() { --m_finishedCount; }
305 void incrementEditableCount() { ++m_editableCount; }
306 void decrementEditableCount() { --m_editableCount; }
307 void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
308 void decrementNonobsoleteCount() { --m_nonobsoleteCount; }
309
310 QString m_context;
311 QString m_comment;
312 QList<MultiMessageItem> m_multiMessageList;
313 QList<ContextItem *> m_contextList;
314 // The next two could be in the MultiMessageItems, but are here for efficiency
315 QList<QList<MessageItem *> > m_messageLists;
316 QList<QList<MessageItem *> *> m_writableMessageLists;
317 int m_finishedCount; // read-write
318 int m_editableCount; // read-write
319 int m_nonobsoleteCount; // all (note: this counts messages, not multi-messages)
320};
321
322
323class MultiDataIndex
324{
325public:
326 MultiDataIndex() : m_model(-1), m_context(-1), m_message(-1) {}
327 MultiDataIndex(int model, int context, int message)
328 : m_model(model), m_context(context), m_message(message) {}
329 void setModel(int model) { m_model = model; }
330 int model() const { return m_model; }
331 int context() const { return m_context; }
332 int message() const { return m_message; }
333 bool isValid() const { return m_context >= 0; }
334 bool operator==(const MultiDataIndex &other) const
335 { return m_model == other.m_model && m_context == other.m_context && m_message == other.m_message; }
336 bool operator!=(const MultiDataIndex &other) const { return !(*this == other); }
337protected:
338 int m_model;
339 int m_context;
340 int m_message;
341};
342
343
344class MultiDataModelIterator : public MultiDataIndex
345{
346public:
347 MultiDataModelIterator(MultiDataModel *model, int modelNo, int contextNo = 0, int messageNo = 0);
348 MessageItem *current() const;
349 bool isValid() const;
350 void operator++();
351private:
352 MultiDataModelIterator() {}
353 MultiDataModel *m_dataModel; // not owned
354};
355
356
357class MessageModel;
358
359class MultiDataModel : public QObject
360{
361 Q_OBJECT
362
363public:
364 MultiDataModel(QObject *parent = 0);
365 ~MultiDataModel();
366
367 bool isWellMergeable(const DataModel *dm) const;
368 void append(DataModel *dm, bool readWrite);
369 bool save(int model, QWidget *parent) { return m_dataModels[model]->save(parent); }
370 bool saveAs(int model, const QString &newFileName, QWidget *parent)
371 { return m_dataModels[model]->saveAs(newFileName, parent); }
372 bool release(int model, const QString &fileName, bool verbose, bool ignoreUnfinished, TranslatorSaveMode mode, QWidget *parent)
373 { return m_dataModels[model]->release(fileName, verbose, ignoreUnfinished, mode, parent); }
374 void close(int model);
375 void closeAll();
376 int isFileLoaded(const QString &name) const;
377 void moveModel(int oldPos, int newPos); // newPos is *before* removing at oldPos; note that this does not emit update signals
378
379 // Entire multi-model
380 int modelCount() const { return m_dataModels.size(); }
381 int contextCount() const { return m_multiContextList.size(); }
382 int messageCount() const { return m_numMessages; }
383 // Next two needed for progress indicator in main window
384 int getNumFinished() const { return m_numFinished; }
385 int getNumEditable() const { return m_numEditable; }
386 bool isModified() const;
387 QStringList srcFileNames(bool pretty = false) const;
388 QString condensedSrcFileNames(bool pretty = false) const;
389
390 // Per submodel
391 QString srcFileName(int model, bool pretty = false) const { return m_dataModels[model]->srcFileName(pretty); }
392 bool isModelWritable(int model) const { return m_dataModels[model]->isWritable(); }
393 bool isModified(int model) const { return m_dataModels[model]->isModified(); }
394 void setModified(int model, bool dirty) { m_dataModels[model]->setModified(dirty); }
395 QLocale::Language language(int model) const { return m_dataModels[model]->language(); }
396 QLocale::Language sourceLanguage(int model) const { return m_dataModels[model]->sourceLanguage(); }
397
398 // Per message
399 void setTranslation(const MultiDataIndex &index, const QString &translation);
400 void setFinished(const MultiDataIndex &index, bool finished);
401 void setDanger(const MultiDataIndex &index, bool danger);
402
403 // Retrieve items
404 DataModel *model(int i) { return m_dataModels[i]; }
405 MultiContextItem *multiContextItem(int ctxIdx) const
406 { return const_cast<MultiContextItem *>(&m_multiContextList[ctxIdx]); }
407 MultiMessageItem *multiMessageItem(const MultiDataIndex &index) const
408 { return multiContextItem(ctxIdx: index.context())->multiMessageItem(msgIdx: index.message()); }
409 MessageItem *messageItem(const MultiDataIndex &index, int model) const;
410 MessageItem *messageItem(const MultiDataIndex &index) const { return messageItem(index, model: index.model()); }
411 int findContextIndex(const QString &context) const;
412 MultiContextItem *findContext(const QString &context) const;
413
414 static QString condenseFileNames(const QStringList &names);
415 static QStringList prettifyFileNames(const QStringList &names);
416
417 QBrush brushForModel(int model) const;
418
419signals:
420 void modelAppended();
421 void modelDeleted(int model);
422 void allModelsDeleted();
423 void languageChanged(int model);
424 void statsChanged(const StatisticalData &newStats);
425 void modifiedChanged(bool);
426 void multiContextDataChanged(const MultiDataIndex &index);
427 void contextDataChanged(const MultiDataIndex &index);
428 void messageDataChanged(const MultiDataIndex &index);
429 void translationChanged(const MultiDataIndex &index); // Only the primary one
430
431private slots:
432 void onModifiedChanged();
433 void onLanguageChanged();
434
435private:
436 friend class MultiDataModelIterator;
437 friend class MessageModel;
438
439 ContextItem *contextItem(const MultiDataIndex &index) const
440 { return multiContextItem(ctxIdx: index.context())->contextItem(model: index.model()); }
441
442 void updateCountsOnAdd(int model, bool writable);
443 void updateCountsOnRemove(int model, bool writable);
444 void incrementFinishedCount() { ++m_numFinished; }
445 void decrementFinishedCount() { --m_numFinished; }
446 void incrementEditableCount() { ++m_numEditable; }
447 void decrementEditableCount() { --m_numEditable; }
448
449 int m_numFinished;
450 int m_numEditable;
451 int m_numMessages;
452
453 bool m_modified;
454
455 QList<MultiContextItem> m_multiContextList;
456 QList<DataModel *> m_dataModels;
457
458 MessageModel *m_msgModel;
459
460 QColor m_colors[7];
461 QBitmap m_bitmap;
462};
463
464class MessageModel : public QAbstractItemModel
465{
466 Q_OBJECT
467
468public:
469 enum { SortRole = Qt::UserRole };
470
471 MessageModel(QObject *parent, MultiDataModel *data);
472
473 // QAbstractItemModel
474 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
475 QModelIndex parent(const QModelIndex& index) const override;
476 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
477 int columnCount(const QModelIndex &parent = QModelIndex()) const override;
478 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
479
480 // Convenience
481 MultiDataIndex dataIndex(const QModelIndex &index, int model) const;
482 MultiDataIndex dataIndex(const QModelIndex &index) const
483 { return dataIndex(index, model: index.column() - 1 < m_data->modelCount() ? index.column() - 1 : -1); }
484 QModelIndex modelIndex(const MultiDataIndex &index);
485
486private slots:
487 void multiContextItemChanged(const MultiDataIndex &index);
488 void contextItemChanged(const MultiDataIndex &index);
489 void messageItemChanged(const MultiDataIndex &index);
490
491private:
492 friend class MultiDataModel;
493
494 MultiDataModel *m_data; // not owned
495};
496
497QT_END_NAMESPACE
498
499#endif // MESSAGEMODEL_H
500

source code of qttools/src/linguist/linguist/messagemodel.h