blob: 4e425ef7ddb5fea474bc3466e469ed59ea4a8373 [file] [log] [blame]
/****************************************************************************
**
** 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $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