1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Linguist of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #ifndef MESSAGEMODEL_H
43 #define MESSAGEMODEL_H
44 
45 #include "translator.h"
46 
47 #include <QtCore/QAbstractItemModel>
48 #include <QtCore/QList>
49 #include <QtCore/QHash>
50 #include <QtCore/QLocale>
51 #include <QtGui/QColor>
52 #include <QtGui/QBitmap>
53 #include <QtXml/QXmlDefaultHandler>
54 
55 
56 QT_BEGIN_NAMESPACE
57 
58 class DataModel;
59 class MultiDataModel;
60 
61 class MessageItem
62 {
63 public:
64     MessageItem(const TranslatorMessage &message);
65 
danger()66     bool danger() const { return m_danger; }
setDanger(bool danger)67     void setDanger(bool danger) { m_danger = danger; }
68 
setTranslation(const QString & translation)69     void setTranslation(const QString &translation)
70         { m_message.setTranslation(translation); }
71 
context()72     QString context() const { return m_message.context(); }
text()73     QString text() const { return m_message.sourceText(); }
pluralText()74     QString pluralText() const { return m_message.extra(QLatin1String("po-msgid_plural")); }
comment()75     QString comment() const { return m_message.comment(); }
fileName()76     QString fileName() const { return m_message.fileName(); }
extraComment()77     QString extraComment() const { return m_message.extraComment(); }
translatorComment()78     QString translatorComment() const { return m_message.translatorComment(); }
setTranslatorComment(const QString & cmt)79     void setTranslatorComment(const QString &cmt) { m_message.setTranslatorComment(cmt); }
lineNumber()80     int lineNumber() const { return m_message.lineNumber(); }
translation()81     QString translation() const { return m_message.translation(); }
translations()82     QStringList translations() const { return m_message.translations(); }
setTranslations(const QStringList & translations)83     void setTranslations(const QStringList &translations)
84         { m_message.setTranslations(translations); }
85 
type()86     TranslatorMessage::Type type() const { return m_message.type(); }
setType(TranslatorMessage::Type type)87     void setType(TranslatorMessage::Type type) { m_message.setType(type); }
88 
isFinished()89     bool isFinished() const { return type() == TranslatorMessage::Finished; }
isObsolete()90     bool isObsolete() const { return type() == TranslatorMessage::Obsolete; }
message()91     const TranslatorMessage &message() const { return m_message; }
92 
93     bool compare(const QString &findText, bool matchSubstring,
94         Qt::CaseSensitivity cs) const;
95 
96 private:
97     TranslatorMessage m_message;
98     bool m_danger;
99 };
100 
101 
102 class ContextItem
103 {
104 public:
105     ContextItem(const QString &context);
106 
finishedDangerCount()107     int finishedDangerCount() const { return m_finishedDangerCount; }
unfinishedDangerCount()108     int unfinishedDangerCount() const { return m_unfinishedDangerCount; }
109 
finishedCount()110     int finishedCount() const { return m_finishedCount; }
unfinishedCount()111     int unfinishedCount() const { return m_nonobsoleteCount - m_finishedCount; }
nonobsoleteCount()112     int nonobsoleteCount() const { return m_nonobsoleteCount; }
113 
context()114     QString context() const { return m_context; }
comment()115     QString comment() const { return m_comment; }
fullContext()116     QString fullContext() const { return m_comment.trimmed(); }
117 
118     // For item status in context list
isObsolete()119     bool isObsolete() const { return !nonobsoleteCount(); }
isFinished()120     bool isFinished() const { return unfinishedCount() == 0; }
121 
122     MessageItem *messageItem(int i) const;
messageCount()123     int messageCount() const { return msgItemList.count(); }
124 
125     MessageItem *findMessage(const QString &sourcetext, const QString &comment) const;
126 
127 private:
128     friend class DataModel;
129     friend class MultiDataModel;
appendMessage(const MessageItem & msg)130     void appendMessage(const MessageItem &msg) { msgItemList.append(msg); }
131     void appendToComment(const QString &x);
incrementFinishedCount()132     void incrementFinishedCount() { ++m_finishedCount; }
decrementFinishedCount()133     void decrementFinishedCount() { --m_finishedCount; }
incrementFinishedDangerCount()134     void incrementFinishedDangerCount() { ++m_finishedDangerCount; }
decrementFinishedDangerCount()135     void decrementFinishedDangerCount() { --m_finishedDangerCount; }
incrementUnfinishedDangerCount()136     void incrementUnfinishedDangerCount() { ++m_unfinishedDangerCount; }
decrementUnfinishedDangerCount()137     void decrementUnfinishedDangerCount() { --m_unfinishedDangerCount; }
incrementNonobsoleteCount()138     void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
139 
140     QString m_comment;
141     QString m_context;
142     int m_finishedCount;
143     int m_finishedDangerCount;
144     int m_unfinishedDangerCount;
145     int m_nonobsoleteCount;
146     QList<MessageItem> msgItemList;
147 };
148 
149 
150 class DataIndex
151 {
152 public:
DataIndex()153     DataIndex() : m_context(-1), m_message(-1) {}
DataIndex(int context,int message)154     DataIndex(int context, int message) : m_context(context), m_message(message) {}
context()155     int context() const { return m_context; }
message()156     int message() const { return m_message; }
isValid()157     bool isValid() const { return m_context >= 0; }
158 protected:
159     int m_context;
160     int m_message;
161 };
162 
163 
164 class DataModelIterator : public DataIndex
165 {
166 public:
167     DataModelIterator(DataModel *model, int contextNo = 0, int messageNo = 0);
168     MessageItem *current() const;
169     bool isValid() const;
170     void operator++();
171 private:
DataModelIterator()172     DataModelIterator() {}
173     DataModel *m_model; // not owned
174 };
175 
176 
177 class DataModel : public QObject
178 {
179     Q_OBJECT
180 public:
181     DataModel(QObject *parent = 0);
182 
183     enum FindLocation { NoLocation = 0, SourceText = 0x1, Translations = 0x2, Comments = 0x4 };
184 
185     // Specializations
contextCount()186     int contextCount() const { return m_contextList.count(); }
187     ContextItem *findContext(const QString &context) const;
188     MessageItem *findMessage(const QString &context, const QString &sourcetext,
189         const QString &comment) const;
190 
191     ContextItem *contextItem(int index) const;
192     MessageItem *messageItem(const DataIndex &index) const;
193 
messageCount()194     int messageCount() const { return m_numMessages; }
isEmpty()195     bool isEmpty() const { return m_numMessages == 0; }
isModified()196     bool isModified() const { return m_modified; }
197     void setModified(bool dirty);
isWritable()198     bool isWritable() const { return m_writable; }
setWritable(bool writable)199     void setWritable(bool writable) { m_writable = writable; }
200 
201     bool isWellMergeable(const DataModel *other) const;
202     bool load(const QString &fileName, bool *langGuessed, QWidget *parent);
save(QWidget * parent)203     bool save(QWidget *parent) { return save(m_srcFileName, parent); }
204     bool saveAs(const QString &newFileName, QWidget *parent);
205     bool release(const QString &fileName, bool verbose,
206         bool ignoreUnfinished, TranslatorSaveMode mode, QWidget *parent);
207     QString srcFileName(bool pretty = false) const
208         { return pretty ? prettifyPlainFileName(m_srcFileName) : m_srcFileName; }
209 
210     static QString prettifyPlainFileName(const QString &fn);
211     static QString prettifyFileName(const QString &fn);
212 
213     bool setLanguageAndCountry(QLocale::Language lang, QLocale::Country country);
language()214     QLocale::Language language() const { return m_language; }
country()215     QLocale::Country country() const { return m_country; }
216     void setSourceLanguageAndCountry(QLocale::Language lang, QLocale::Country country);
sourceLanguage()217     QLocale::Language sourceLanguage() const { return m_sourceLanguage; }
sourceCountry()218     QLocale::Country sourceCountry() const { return m_sourceCountry; }
codecName()219     QByteArray codecName() const { return m_codecName; }
220 
localizedLanguage()221     const QString &localizedLanguage() const { return m_localizedLanguage; }
numerusForms()222     const QStringList &numerusForms() const { return m_numerusForms; }
countRefNeeds()223     const QList<bool> &countRefNeeds() const { return m_countRefNeeds; }
224 
225     QStringList normalizedTranslations(const MessageItem &m) const;
226     void doCharCounting(const QString& text, int& trW, int& trC, int& trCS);
227     void updateStatistics();
228 
getSrcWords()229     int getSrcWords() const { return m_srcWords; }
getSrcChars()230     int getSrcChars() const { return m_srcChars; }
getSrcCharsSpc()231     int getSrcCharsSpc() const { return m_srcCharsSpc; }
232 
233 signals:
234     void statsChanged(int words, int characters, int cs, int words2, int characters2, int cs2);
235     void progressChanged(int finishedCount, int oldFinishedCount);
236     void languageChanged();
237     void modifiedChanged();
238 
239 private:
240     friend class DataModelIterator;
241     QList<ContextItem> m_contextList;
242 
243     bool save(const QString &fileName, QWidget *parent);
244     void updateLocale();
245 
246     bool m_writable;
247     bool m_modified;
248 
249     int m_numMessages;
250 
251     // For statistics
252     int m_srcWords;
253     int m_srcChars;
254     int m_srcCharsSpc;
255 
256     QString m_srcFileName;
257     QLocale::Language m_language;
258     QLocale::Language m_sourceLanguage;
259     QLocale::Country m_country;
260     QLocale::Country m_sourceCountry;
261     QByteArray m_codecName;
262     bool m_relativeLocations;
263     Translator::ExtraData m_extra;
264 
265     QString m_localizedLanguage;
266     QStringList m_numerusForms;
267     QList<bool> m_countRefNeeds;
268 };
269 
270 
271 struct MultiMessageItem
272 {
273 public:
274     MultiMessageItem(const MessageItem *m);
textMultiMessageItem275     QString text() const { return m_text; }
pluralTextMultiMessageItem276     QString pluralText() const { return m_pluralText; }
commentMultiMessageItem277     QString comment() const { return m_comment; }
isEmptyMultiMessageItem278     bool isEmpty() const { return !m_nonnullCount; }
279     // The next two include also read-only
isObsoleteMultiMessageItem280     bool isObsolete() const { return m_nonnullCount && !m_nonobsoleteCount; }
countNonobsoleteMultiMessageItem281     int countNonobsolete() const { return m_nonobsoleteCount; }
282     // The next three include only read-write
countEditableMultiMessageItem283     int countEditable() const { return m_editableCount; }
isUnfinishedMultiMessageItem284     bool isUnfinished() const { return m_unfinishedCount != 0; }
countUnfinishedMultiMessageItem285     int countUnfinished() const { return m_unfinishedCount; }
286 
287 private:
288     friend class MultiDataModel;
incrementNonnullCountMultiMessageItem289     void incrementNonnullCount() { ++m_nonnullCount; }
decrementNonnullCountMultiMessageItem290     void decrementNonnullCount() { --m_nonnullCount; }
incrementNonobsoleteCountMultiMessageItem291     void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
decrementNonobsoleteCountMultiMessageItem292     void decrementNonobsoleteCount() { --m_nonobsoleteCount; }
incrementEditableCountMultiMessageItem293     void incrementEditableCount() { ++m_editableCount; }
decrementEditableCountMultiMessageItem294     void decrementEditableCount() { --m_editableCount; }
incrementUnfinishedCountMultiMessageItem295     void incrementUnfinishedCount() { ++m_unfinishedCount; }
decrementUnfinishedCountMultiMessageItem296     void decrementUnfinishedCount() { --m_unfinishedCount; }
297 
298     QString m_text;
299     QString m_pluralText;
300     QString m_comment;
301     int m_nonnullCount; // all
302     int m_nonobsoleteCount; // all
303     int m_editableCount; // read-write
304     int m_unfinishedCount; // read-write
305 };
306 
307 struct MultiContextItem
308 {
309 public:
310     MultiContextItem(int oldCount, ContextItem *ctx, bool writable);
311 
contextItemMultiContextItem312     ContextItem *contextItem(int model) const { return m_contextList[model]; }
313 
multiMessageItemMultiContextItem314     MultiMessageItem *multiMessageItem(int msgIdx) const
315         { return const_cast<MultiMessageItem *>(&m_multiMessageList[msgIdx]); }
messageItemMultiContextItem316     MessageItem *messageItem(int model, int msgIdx) const { return m_messageLists[model][msgIdx]; }
317     int firstNonobsoleteMessageIndex(int msgIdx) const;
318     int findMessage(const QString &sourcetext, const QString &comment) const;
319 
contextMultiContextItem320     QString context() const { return m_context; }
commentMultiContextItem321     QString comment() const { return m_comment; }
messageCountMultiContextItem322     int messageCount() const { return m_messageLists.isEmpty() ? 0 : m_messageLists[0].count(); }
323     // For item count in context list
getNumFinishedMultiContextItem324     int getNumFinished() const { return m_finishedCount; }
getNumEditableMultiContextItem325     int getNumEditable() const { return m_editableCount; }
326     // For background in context list
isObsoleteMultiContextItem327     bool isObsolete() const { return messageCount() && !m_nonobsoleteCount; }
328 
329 private:
330     friend class MultiDataModel;
331     void appendEmptyModel();
332     void assignLastModel(ContextItem *ctx, bool writable);
333     void removeModel(int pos);
334     void moveModel(int oldPos, int newPos); // newPos is *before* removing at oldPos
335     void putMessageItem(int pos, MessageItem *m);
336     void appendMessageItems(const QList<MessageItem *> &m);
337     void removeMultiMessageItem(int pos);
incrementFinishedCountMultiContextItem338     void incrementFinishedCount() { ++m_finishedCount; }
decrementFinishedCountMultiContextItem339     void decrementFinishedCount() { --m_finishedCount; }
incrementEditableCountMultiContextItem340     void incrementEditableCount() { ++m_editableCount; }
decrementEditableCountMultiContextItem341     void decrementEditableCount() { --m_editableCount; }
incrementNonobsoleteCountMultiContextItem342     void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
decrementNonobsoleteCountMultiContextItem343     void decrementNonobsoleteCount() { --m_nonobsoleteCount; }
344 
345     QString m_context;
346     QString m_comment;
347     QList<MultiMessageItem> m_multiMessageList;
348     QList<ContextItem *> m_contextList;
349     // The next two could be in the MultiMessageItems, but are here for efficiency
350     QList<QList<MessageItem *> > m_messageLists;
351     QList<QList<MessageItem *> *> m_writableMessageLists;
352     int m_finishedCount; // read-write
353     int m_editableCount; // read-write
354     int m_nonobsoleteCount; // all (note: this counts messages, not multi-messages)
355 };
356 
357 
358 class MultiDataIndex
359 {
360 public:
MultiDataIndex()361     MultiDataIndex() : m_model(-1), m_context(-1), m_message(-1) {}
MultiDataIndex(int model,int context,int message)362     MultiDataIndex(int model, int context, int message)
363         : m_model(model), m_context(context), m_message(message) {}
setModel(int model)364     void setModel(int model) { m_model = model; }
model()365     int model() const { return m_model; }
context()366     int context() const { return m_context; }
message()367     int message() const { return m_message; }
isValid()368     bool isValid() const { return m_context >= 0; }
369     bool operator==(const MultiDataIndex &other) const
370         { return m_model == other.m_model && m_context == other.m_context && m_message == other.m_message; }
371     bool operator!=(const MultiDataIndex &other) const { return !(*this == other); }
372 protected:
373     int m_model;
374     int m_context;
375     int m_message;
376 };
377 
378 
379 class MultiDataModelIterator : public MultiDataIndex
380 {
381 public:
382     MultiDataModelIterator(MultiDataModel *model, int modelNo, int contextNo = 0, int messageNo = 0);
383     MessageItem *current() const;
384     bool isValid() const;
385     void operator++();
386 private:
MultiDataModelIterator()387     MultiDataModelIterator() {}
388     MultiDataModel *m_dataModel; // not owned
389 };
390 
391 
392 class MessageModel;
393 
394 class MultiDataModel : public QObject
395 {
396     Q_OBJECT
397 
398 public:
399     MultiDataModel(QObject *parent = 0);
400     ~MultiDataModel();
401 
402     bool isWellMergeable(const DataModel *dm) const;
403     void append(DataModel *dm, bool readWrite);
save(int model,QWidget * parent)404     bool save(int model, QWidget *parent) { return m_dataModels[model]->save(parent); }
saveAs(int model,const QString & newFileName,QWidget * parent)405     bool saveAs(int model, const QString &newFileName, QWidget *parent)
406         { return m_dataModels[model]->saveAs(newFileName, parent); }
release(int model,const QString & fileName,bool verbose,bool ignoreUnfinished,TranslatorSaveMode mode,QWidget * parent)407     bool release(int model, const QString &fileName, bool verbose, bool ignoreUnfinished, TranslatorSaveMode mode, QWidget *parent)
408         { return m_dataModels[model]->release(fileName, verbose, ignoreUnfinished, mode, parent); }
409     void close(int model);
410     void closeAll();
411     int isFileLoaded(const QString &name) const;
412     void moveModel(int oldPos, int newPos); // newPos is *before* removing at oldPos; note that this does not emit update signals
413 
414     // Entire multi-model
modelCount()415     int modelCount() const { return m_dataModels.count(); }
contextCount()416     int contextCount() const { return m_multiContextList.count(); }
messageCount()417     int messageCount() const { return m_numMessages; }
418     // Next two needed for progress indicator in main window
getNumFinished()419     int getNumFinished() const { return m_numFinished; }
getNumEditable()420     int getNumEditable() const { return m_numEditable; }
421     bool isModified() const;
422     QStringList srcFileNames(bool pretty = false) const;
423     QString condensedSrcFileNames(bool pretty = false) const;
424 
425     // Per submodel
426     QString srcFileName(int model, bool pretty = false) const { return m_dataModels[model]->srcFileName(pretty); }
isModelWritable(int model)427     bool isModelWritable(int model) const { return m_dataModels[model]->isWritable(); }
isModified(int model)428     bool isModified(int model) const { return m_dataModels[model]->isModified(); }
setModified(int model,bool dirty)429     void setModified(int model, bool dirty) { m_dataModels[model]->setModified(dirty); }
language(int model)430     QLocale::Language language(int model) const { return m_dataModels[model]->language(); }
sourceLanguage(int model)431     QLocale::Language sourceLanguage(int model) const { return m_dataModels[model]->sourceLanguage(); }
432 
433     // Per message
434     void setTranslation(const MultiDataIndex &index, const QString &translation);
435     void setFinished(const MultiDataIndex &index, bool finished);
436     void setDanger(const MultiDataIndex &index, bool danger);
437 
438     // Retrieve items
model(int i)439     DataModel *model(int i) { return m_dataModels[i]; }
multiContextItem(int ctxIdx)440     MultiContextItem *multiContextItem(int ctxIdx) const
441         { return const_cast<MultiContextItem *>(&m_multiContextList[ctxIdx]); }
multiMessageItem(const MultiDataIndex & index)442     MultiMessageItem *multiMessageItem(const MultiDataIndex &index) const
443         { return multiContextItem(index.context())->multiMessageItem(index.message()); }
444     MessageItem *messageItem(const MultiDataIndex &index, int model) const;
messageItem(const MultiDataIndex & index)445     MessageItem *messageItem(const MultiDataIndex &index) const { return messageItem(index, index.model()); }
446 
447     static QString condenseFileNames(const QStringList &names);
448     static QStringList prettifyFileNames(const QStringList &names);
449 
450     QBrush brushForModel(int model) const;
451 
452 signals:
453     void modelAppended();
454     void modelDeleted(int model);
455     void allModelsDeleted();
456     void languageChanged(int model);
457     void statsChanged(int words, int characters, int cs, int words2, int characters2, int cs2);
458     void modifiedChanged(bool);
459     void multiContextDataChanged(const MultiDataIndex &index);
460     void contextDataChanged(const MultiDataIndex &index);
461     void messageDataChanged(const MultiDataIndex &index);
462     void translationChanged(const MultiDataIndex &index); // Only the primary one
463 
464 private slots:
465     void onModifiedChanged();
466     void onLanguageChanged();
467 
468 private:
469     friend class MultiDataModelIterator;
470     friend class MessageModel;
471 
472     int findContextIndex(const QString &context) const;
473     MultiContextItem *findContext(const QString &context) const;
474 
contextItem(const MultiDataIndex & index)475     ContextItem *contextItem(const MultiDataIndex &index) const
476         { return multiContextItem(index.context())->contextItem(index.model()); }
477 
478     void updateCountsOnAdd(int model, bool writable);
479     void updateCountsOnRemove(int model, bool writable);
incrementFinishedCount()480     void incrementFinishedCount() { ++m_numFinished; }
decrementFinishedCount()481     void decrementFinishedCount() { --m_numFinished; }
incrementEditableCount()482     void incrementEditableCount() { ++m_numEditable; }
decrementEditableCount()483     void decrementEditableCount() { --m_numEditable; }
484 
485     int m_numFinished;
486     int m_numEditable;
487     int m_numMessages;
488 
489     bool m_modified;
490 
491     QList<MultiContextItem> m_multiContextList;
492     QList<DataModel *> m_dataModels;
493 
494     MessageModel *m_msgModel;
495 
496     QColor m_colors[7];
497     QBitmap m_bitmap;
498 };
499 
500 class MessageModel : public QAbstractItemModel
501 {
502     Q_OBJECT
503 
504 public:
505     enum { SortRole = Qt::UserRole };
506 
507     MessageModel(QObject *parent, MultiDataModel *data);
508 
509     // QAbstractItemModel
510     QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
511     QModelIndex parent(const QModelIndex& index) const;
512     int rowCount(const QModelIndex &parent = QModelIndex()) const;
513     int columnCount(const QModelIndex &parent = QModelIndex()) const;
514     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
515 
516     // Convenience
517     MultiDataIndex dataIndex(const QModelIndex &index, int model) const;
dataIndex(const QModelIndex & index)518     MultiDataIndex dataIndex(const QModelIndex &index) const
519         { return dataIndex(index, index.column() - 1 < m_data->modelCount() ? index.column() - 1 : -1); }
520     QModelIndex modelIndex(const MultiDataIndex &index);
521 
522 private slots:
reset()523     void reset() { QAbstractItemModel::reset(); }
524     void multiContextItemChanged(const MultiDataIndex &index);
525     void contextItemChanged(const MultiDataIndex &index);
526     void messageItemChanged(const MultiDataIndex &index);
527 
528 private:
529     friend class MultiDataModel;
530 
531     MultiDataModel *m_data; // not owned
532 };
533 
534 QT_END_NAMESPACE
535 
536 #endif // MESSAGEMODEL_H
537