diff options
Diffstat (limited to 'src/linguist/linguist/messagemodel.h')
-rw-r--r-- | src/linguist/linguist/messagemodel.h | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/src/linguist/linguist/messagemodel.h b/src/linguist/linguist/messagemodel.h new file mode 100644 index 000000000..d8ca9d8e8 --- /dev/null +++ b/src/linguist/linguist/messagemodel.h @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MESSAGEMODEL_H +#define MESSAGEMODEL_H + +#include "translator.h" + +#include <QtCore/QAbstractItemModel> +#include <QtCore/QList> +#include <QtCore/QHash> +#include <QtCore/QLocale> +#include <QtGui/QColor> +#include <QtGui/QBitmap> +#include <QtXml/QXmlDefaultHandler> + + +QT_BEGIN_NAMESPACE + +class DataModel; +class MultiDataModel; + +class MessageItem +{ +public: + MessageItem(const TranslatorMessage &message); + + bool danger() const { return m_danger; } + void setDanger(bool danger) { m_danger = danger; } + + void setTranslation(const QString &translation) + { m_message.setTranslation(translation); } + + QString context() const { return m_message.context(); } + QString text() const { return m_message.sourceText(); } + QString pluralText() const { return m_message.extra(QLatin1String("po-msgid_plural")); } + QString comment() const { return m_message.comment(); } + QString fileName() const { return m_message.fileName(); } + QString extraComment() const { return m_message.extraComment(); } + QString translatorComment() const { return m_message.translatorComment(); } + void setTranslatorComment(const QString &cmt) { m_message.setTranslatorComment(cmt); } + int lineNumber() const { return m_message.lineNumber(); } + QString translation() const { return m_message.translation(); } + QStringList translations() const { return m_message.translations(); } + void setTranslations(const QStringList &translations) + { m_message.setTranslations(translations); } + + TranslatorMessage::Type type() const { return m_message.type(); } + void setType(TranslatorMessage::Type type) { m_message.setType(type); } + + bool isFinished() const { return type() == TranslatorMessage::Finished; } + bool isObsolete() const { return type() == TranslatorMessage::Obsolete; } + const TranslatorMessage &message() const { return m_message; } + + bool compare(const QString &findText, bool matchSubstring, + Qt::CaseSensitivity cs) const; + +private: + TranslatorMessage m_message; + bool m_danger; +}; + + +class ContextItem +{ +public: + ContextItem(const QString &context); + + int finishedDangerCount() const { return m_finishedDangerCount; } + int unfinishedDangerCount() const { return m_unfinishedDangerCount; } + + int finishedCount() const { return m_finishedCount; } + int unfinishedCount() const { return m_nonobsoleteCount - m_finishedCount; } + int nonobsoleteCount() const { return m_nonobsoleteCount; } + + QString context() const { return m_context; } + QString comment() const { return m_comment; } + QString fullContext() const { return m_comment.trimmed(); } + + // For item status in context list + bool isObsolete() const { return !nonobsoleteCount(); } + bool isFinished() const { return unfinishedCount() == 0; } + + MessageItem *messageItem(int i) const; + int messageCount() const { return msgItemList.count(); } + + MessageItem *findMessage(const QString &sourcetext, const QString &comment) const; + +private: + friend class DataModel; + friend class MultiDataModel; + void appendMessage(const MessageItem &msg) { msgItemList.append(msg); } + void appendToComment(const QString &x); + void incrementFinishedCount() { ++m_finishedCount; } + void decrementFinishedCount() { --m_finishedCount; } + void incrementFinishedDangerCount() { ++m_finishedDangerCount; } + void decrementFinishedDangerCount() { --m_finishedDangerCount; } + void incrementUnfinishedDangerCount() { ++m_unfinishedDangerCount; } + void decrementUnfinishedDangerCount() { --m_unfinishedDangerCount; } + void incrementNonobsoleteCount() { ++m_nonobsoleteCount; } + + QString m_comment; + QString m_context; + int m_finishedCount; + int m_finishedDangerCount; + int m_unfinishedDangerCount; + int m_nonobsoleteCount; + QList<MessageItem> msgItemList; +}; + + +class DataIndex +{ +public: + DataIndex() : m_context(-1), m_message(-1) {} + DataIndex(int context, int message) : m_context(context), m_message(message) {} + int context() const { return m_context; } + int message() const { return m_message; } + bool isValid() const { return m_context >= 0; } +protected: + int m_context; + int m_message; +}; + + +class DataModelIterator : public DataIndex +{ +public: + DataModelIterator(DataModel *model, int contextNo = 0, int messageNo = 0); + MessageItem *current() const; + bool isValid() const; + void operator++(); +private: + DataModelIterator() {} + DataModel *m_model; // not owned +}; + + +class DataModel : public QObject +{ + Q_OBJECT +public: + DataModel(QObject *parent = 0); + + enum FindLocation { NoLocation = 0, SourceText = 0x1, Translations = 0x2, Comments = 0x4 }; + + // Specializations + int contextCount() const { return m_contextList.count(); } + ContextItem *findContext(const QString &context) const; + MessageItem *findMessage(const QString &context, const QString &sourcetext, + const QString &comment) const; + + ContextItem *contextItem(int index) const; + MessageItem *messageItem(const DataIndex &index) const; + + int messageCount() const { return m_numMessages; } + bool isEmpty() const { return m_numMessages == 0; } + bool isModified() const { return m_modified; } + void setModified(bool dirty); + bool isWritable() const { return m_writable; } + void setWritable(bool writable) { m_writable = writable; } + + bool isWellMergeable(const DataModel *other) const; + bool load(const QString &fileName, bool *langGuessed, QWidget *parent); + bool save(QWidget *parent) { return save(m_srcFileName, parent); } + bool saveAs(const QString &newFileName, QWidget *parent); + bool release(const QString &fileName, bool verbose, + bool ignoreUnfinished, TranslatorSaveMode mode, QWidget *parent); + QString srcFileName(bool pretty = false) const + { return pretty ? prettifyPlainFileName(m_srcFileName) : m_srcFileName; } + + static QString prettifyPlainFileName(const QString &fn); + static QString prettifyFileName(const QString &fn); + + bool setLanguageAndCountry(QLocale::Language lang, QLocale::Country country); + QLocale::Language language() const { return m_language; } + QLocale::Country country() const { return m_country; } + void setSourceLanguageAndCountry(QLocale::Language lang, QLocale::Country country); + QLocale::Language sourceLanguage() const { return m_sourceLanguage; } + QLocale::Country sourceCountry() const { return m_sourceCountry; } + + const QString &localizedLanguage() const { return m_localizedLanguage; } + const QStringList &numerusForms() const { return m_numerusForms; } + const QList<bool> &countRefNeeds() const { return m_countRefNeeds; } + + QStringList normalizedTranslations(const MessageItem &m) const; + void doCharCounting(const QString& text, int& trW, int& trC, int& trCS); + void updateStatistics(); + + int getSrcWords() const { return m_srcWords; } + int getSrcChars() const { return m_srcChars; } + int getSrcCharsSpc() const { return m_srcCharsSpc; } + +signals: + void statsChanged(int words, int characters, int cs, int words2, int characters2, int cs2); + void progressChanged(int finishedCount, int oldFinishedCount); + void languageChanged(); + void modifiedChanged(); + +private: + friend class DataModelIterator; + QList<ContextItem> m_contextList; + + bool save(const QString &fileName, QWidget *parent); + void updateLocale(); + + bool m_writable; + bool m_modified; + + int m_numMessages; + + // For statistics + int m_srcWords; + int m_srcChars; + int m_srcCharsSpc; + + QString m_srcFileName; + QLocale::Language m_language; + QLocale::Language m_sourceLanguage; + QLocale::Country m_country; + QLocale::Country m_sourceCountry; + QByteArray m_codecName; + bool m_relativeLocations; + Translator::ExtraData m_extra; + + QString m_localizedLanguage; + QStringList m_numerusForms; + QList<bool> m_countRefNeeds; +}; + + +struct MultiMessageItem +{ +public: + MultiMessageItem(const MessageItem *m); + QString text() const { return m_text; } + QString pluralText() const { return m_pluralText; } + QString comment() const { return m_comment; } + bool isEmpty() const { return !m_nonnullCount; } + // The next two include also read-only + bool isObsolete() const { return m_nonnullCount && !m_nonobsoleteCount; } + int countNonobsolete() const { return m_nonobsoleteCount; } + // The next three include only read-write + int countEditable() const { return m_editableCount; } + bool isUnfinished() const { return m_unfinishedCount != 0; } + int countUnfinished() const { return m_unfinishedCount; } + +private: + friend class MultiDataModel; + void incrementNonnullCount() { ++m_nonnullCount; } + void decrementNonnullCount() { --m_nonnullCount; } + void incrementNonobsoleteCount() { ++m_nonobsoleteCount; } + void decrementNonobsoleteCount() { --m_nonobsoleteCount; } + void incrementEditableCount() { ++m_editableCount; } + void decrementEditableCount() { --m_editableCount; } + void incrementUnfinishedCount() { ++m_unfinishedCount; } + void decrementUnfinishedCount() { --m_unfinishedCount; } + + QString m_text; + QString m_pluralText; + QString m_comment; + int m_nonnullCount; // all + int m_nonobsoleteCount; // all + int m_editableCount; // read-write + int m_unfinishedCount; // read-write +}; + +struct MultiContextItem +{ +public: + MultiContextItem(int oldCount, ContextItem *ctx, bool writable); + + ContextItem *contextItem(int model) const { return m_contextList[model]; } + + MultiMessageItem *multiMessageItem(int msgIdx) const + { return const_cast<MultiMessageItem *>(&m_multiMessageList[msgIdx]); } + MessageItem *messageItem(int model, int msgIdx) const { return m_messageLists[model][msgIdx]; } + int firstNonobsoleteMessageIndex(int msgIdx) const; + int findMessage(const QString &sourcetext, const QString &comment) const; + + QString context() const { return m_context; } + QString comment() const { return m_comment; } + int messageCount() const { return m_messageLists.isEmpty() ? 0 : m_messageLists[0].count(); } + // For item count in context list + int getNumFinished() const { return m_finishedCount; } + int getNumEditable() const { return m_editableCount; } + // For background in context list + bool isObsolete() const { return messageCount() && !m_nonobsoleteCount; } + +private: + friend class MultiDataModel; + void appendEmptyModel(); + void assignLastModel(ContextItem *ctx, bool writable); + void removeModel(int pos); + void moveModel(int oldPos, int newPos); // newPos is *before* removing at oldPos + void putMessageItem(int pos, MessageItem *m); + void appendMessageItems(const QList<MessageItem *> &m); + void removeMultiMessageItem(int pos); + void incrementFinishedCount() { ++m_finishedCount; } + void decrementFinishedCount() { --m_finishedCount; } + void incrementEditableCount() { ++m_editableCount; } + void decrementEditableCount() { --m_editableCount; } + void incrementNonobsoleteCount() { ++m_nonobsoleteCount; } + void decrementNonobsoleteCount() { --m_nonobsoleteCount; } + + QString m_context; + QString m_comment; + QList<MultiMessageItem> m_multiMessageList; + QList<ContextItem *> m_contextList; + // The next two could be in the MultiMessageItems, but are here for efficiency + QList<QList<MessageItem *> > m_messageLists; + QList<QList<MessageItem *> *> m_writableMessageLists; + int m_finishedCount; // read-write + int m_editableCount; // read-write + int m_nonobsoleteCount; // all (note: this counts messages, not multi-messages) +}; + + +class MultiDataIndex +{ +public: + MultiDataIndex() : m_model(-1), m_context(-1), m_message(-1) {} + MultiDataIndex(int model, int context, int message) + : m_model(model), m_context(context), m_message(message) {} + void setModel(int model) { m_model = model; } + int model() const { return m_model; } + int context() const { return m_context; } + int message() const { return m_message; } + bool isValid() const { return m_context >= 0; } + bool operator==(const MultiDataIndex &other) const + { return m_model == other.m_model && m_context == other.m_context && m_message == other.m_message; } + bool operator!=(const MultiDataIndex &other) const { return !(*this == other); } +protected: + int m_model; + int m_context; + int m_message; +}; + + +class MultiDataModelIterator : public MultiDataIndex +{ +public: + MultiDataModelIterator(MultiDataModel *model, int modelNo, int contextNo = 0, int messageNo = 0); + MessageItem *current() const; + bool isValid() const; + void operator++(); +private: + MultiDataModelIterator() {} + MultiDataModel *m_dataModel; // not owned +}; + + +class MessageModel; + +class MultiDataModel : public QObject +{ + Q_OBJECT + +public: + MultiDataModel(QObject *parent = 0); + ~MultiDataModel(); + + bool isWellMergeable(const DataModel *dm) const; + void append(DataModel *dm, bool readWrite); + bool save(int model, QWidget *parent) { return m_dataModels[model]->save(parent); } + bool saveAs(int model, const QString &newFileName, QWidget *parent) + { return m_dataModels[model]->saveAs(newFileName, parent); } + bool release(int model, const QString &fileName, bool verbose, bool ignoreUnfinished, TranslatorSaveMode mode, QWidget *parent) + { return m_dataModels[model]->release(fileName, verbose, ignoreUnfinished, mode, parent); } + void close(int model); + void closeAll(); + int isFileLoaded(const QString &name) const; + void moveModel(int oldPos, int newPos); // newPos is *before* removing at oldPos; note that this does not emit update signals + + // Entire multi-model + int modelCount() const { return m_dataModels.count(); } + int contextCount() const { return m_multiContextList.count(); } + int messageCount() const { return m_numMessages; } + // Next two needed for progress indicator in main window + int getNumFinished() const { return m_numFinished; } + int getNumEditable() const { return m_numEditable; } + bool isModified() const; + QStringList srcFileNames(bool pretty = false) const; + QString condensedSrcFileNames(bool pretty = false) const; + + // Per submodel + QString srcFileName(int model, bool pretty = false) const { return m_dataModels[model]->srcFileName(pretty); } + bool isModelWritable(int model) const { return m_dataModels[model]->isWritable(); } + bool isModified(int model) const { return m_dataModels[model]->isModified(); } + void setModified(int model, bool dirty) { m_dataModels[model]->setModified(dirty); } + QLocale::Language language(int model) const { return m_dataModels[model]->language(); } + QLocale::Language sourceLanguage(int model) const { return m_dataModels[model]->sourceLanguage(); } + + // Per message + void setTranslation(const MultiDataIndex &index, const QString &translation); + void setFinished(const MultiDataIndex &index, bool finished); + void setDanger(const MultiDataIndex &index, bool danger); + + // Retrieve items + DataModel *model(int i) { return m_dataModels[i]; } + MultiContextItem *multiContextItem(int ctxIdx) const + { return const_cast<MultiContextItem *>(&m_multiContextList[ctxIdx]); } + MultiMessageItem *multiMessageItem(const MultiDataIndex &index) const + { return multiContextItem(index.context())->multiMessageItem(index.message()); } + MessageItem *messageItem(const MultiDataIndex &index, int model) const; + MessageItem *messageItem(const MultiDataIndex &index) const { return messageItem(index, index.model()); } + + static QString condenseFileNames(const QStringList &names); + static QStringList prettifyFileNames(const QStringList &names); + + QBrush brushForModel(int model) const; + +signals: + void modelAppended(); + void modelDeleted(int model); + void allModelsDeleted(); + void languageChanged(int model); + void statsChanged(int words, int characters, int cs, int words2, int characters2, int cs2); + void modifiedChanged(bool); + void multiContextDataChanged(const MultiDataIndex &index); + void contextDataChanged(const MultiDataIndex &index); + void messageDataChanged(const MultiDataIndex &index); + void translationChanged(const MultiDataIndex &index); // Only the primary one + +private slots: + void onModifiedChanged(); + void onLanguageChanged(); + +private: + friend class MultiDataModelIterator; + friend class MessageModel; + + int findContextIndex(const QString &context) const; + MultiContextItem *findContext(const QString &context) const; + + ContextItem *contextItem(const MultiDataIndex &index) const + { return multiContextItem(index.context())->contextItem(index.model()); } + + void updateCountsOnAdd(int model, bool writable); + void updateCountsOnRemove(int model, bool writable); + void incrementFinishedCount() { ++m_numFinished; } + void decrementFinishedCount() { --m_numFinished; } + void incrementEditableCount() { ++m_numEditable; } + void decrementEditableCount() { --m_numEditable; } + + int m_numFinished; + int m_numEditable; + int m_numMessages; + + bool m_modified; + + QList<MultiContextItem> m_multiContextList; + QList<DataModel *> m_dataModels; + + MessageModel *m_msgModel; + + QColor m_colors[7]; + QBitmap m_bitmap; +}; + +class MessageModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + enum { SortRole = Qt::UserRole }; + + MessageModel(QObject *parent, MultiDataModel *data); + + // QAbstractItemModel + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + // Convenience + MultiDataIndex dataIndex(const QModelIndex &index, int model) const; + MultiDataIndex dataIndex(const QModelIndex &index) const + { return dataIndex(index, index.column() - 1 < m_data->modelCount() ? index.column() - 1 : -1); } + QModelIndex modelIndex(const MultiDataIndex &index); + +private slots: + void reset() { QAbstractItemModel::reset(); } + void multiContextItemChanged(const MultiDataIndex &index); + void contextItemChanged(const MultiDataIndex &index); + void messageItemChanged(const MultiDataIndex &index); + +private: + friend class MultiDataModel; + + MultiDataModel *m_data; // not owned +}; + +QT_END_NAMESPACE + +#endif // MESSAGEMODEL_H |