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