1 /*
2     SPDX-FileCopyrightText: 2013 Andreas Cord-Landwehr <cordlandwehr@kde.org>
3 
4     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6 
7 #include "phraselistmodel.h"
8 #include "core/unit.h"
9 #include <KLocalizedString>
10 #include <QSignalMapper>
11 
PhraseListModel(QObject * parent)12 PhraseListModel::PhraseListModel(QObject *parent)
13     : QAbstractListModel(parent)
14     , m_unit(nullptr)
15     , m_signalMapper(new QSignalMapper(this))
16 {
17     connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(emitPhraseChanged(int)));
18 
19     // connect all phrase number operations to single signal
20     connect(this, &PhraseListModel::typeChanged, this, &PhraseListModel::countChanged);
21     connect(this, &PhraseListModel::unitChanged, this, &PhraseListModel::countChanged);
22 }
23 
roleNames() const24 QHash<int, QByteArray> PhraseListModel::roleNames() const
25 {
26     QHash<int, QByteArray> roles;
27     roles[TextRole] = "text";
28     roles[SoundFileRole] = "soundFile";
29     roles[IdRole] = "id";
30     roles[TypeRole] = "type";
31     roles[ExcludedRole] = "excludedRole";
32     roles[DataRole] = "dataRole";
33 
34     return roles;
35 }
36 
setUnit(Unit * unit)37 void PhraseListModel::setUnit(Unit *unit)
38 {
39     if (m_unit == unit) {
40         return;
41     }
42 
43     beginResetModel();
44 
45     if (m_unit) {
46         m_unit->disconnect(this);
47         for (auto &phrase : m_unit->phrases()) {
48             phrase->disconnect(this);
49         }
50     }
51 
52     m_unit = unit;
53     if (m_unit) {
54         // initial setting of signal mappings
55         connect(m_unit, &Unit::phraseAboutToBeAdded, this, &PhraseListModel::onPhraseAboutToBeAdded);
56         connect(m_unit, &Unit::phraseAdded, this, &PhraseListModel::onPhraseAdded);
57         connect(m_unit, &Unit::phraseAboutToBeRemoved, this, &PhraseListModel::onPhraseAboutToBeRemoved);
58         connect(m_unit, &Unit::phraseRemoved, this, &PhraseListModel::onPhrasesRemoved);
59 
60         // insert and connect all already existing phrases
61         int phrases = m_unit->phrases().count();
62         for (int i = 0; i < phrases; ++i) {
63             onPhraseAboutToBeAdded(m_unit->phrases().at(i), i);
64             endInsertRows();
65             emit countChanged();
66         }
67         updateMappings();
68     }
69 
70     // emit done
71     endResetModel();
72     emit unitChanged();
73 }
74 
unit() const75 Unit *PhraseListModel::unit() const
76 {
77     return m_unit;
78 }
79 
data(const QModelIndex & index,int role) const80 QVariant PhraseListModel::data(const QModelIndex &index, int role) const
81 {
82     Q_ASSERT(m_unit);
83 
84     if (!index.isValid()) {
85         return QVariant();
86     }
87 
88     if (index.row() >= m_unit->phrases().count()) {
89         return QVariant();
90     }
91 
92     std::shared_ptr<IPhrase> const phrase = m_unit->phrases().at(index.row());
93 
94     switch (role) {
95         case Qt::DisplayRole:
96             return !phrase->text().isEmpty() ? QVariant(phrase->text()) : QVariant(i18nc("@item:inlistbox:", "unknown"));
97         case Qt::ToolTipRole:
98             return QVariant(phrase->text());
99         case TextRole:
100             return phrase->text();
101         case SoundFileRole:
102             return phrase->sound();
103         case IdRole:
104             return phrase->id();
105         case TypeRole:
106             return QVariant::fromValue<IPhrase::Type>(phrase->type());
107             //    case ExcludedRole: //FIXME
108             //        return phrase->isExcluded();
109         case DataRole:
110             return QVariant::fromValue<QObject *>(phrase.get());
111         default:
112             return QVariant();
113     }
114 }
115 
rowCount(const QModelIndex & parent) const116 int PhraseListModel::rowCount(const QModelIndex &parent) const
117 {
118     if (!m_unit) {
119         return 0;
120     }
121 
122     if (parent.isValid()) {
123         return 0;
124     }
125     return m_unit->phrases().count();
126 }
127 
onPhraseAboutToBeAdded(std::shared_ptr<IPhrase> phrase,int index)128 void PhraseListModel::onPhraseAboutToBeAdded(std::shared_ptr<IPhrase> phrase, int index)
129 {
130     connect(phrase.get(), SIGNAL(textChanged()), m_signalMapper, SLOT(map()));
131     connect(phrase.get(), SIGNAL(typeChanged()), m_signalMapper, SLOT(map()));
132     connect(phrase.get(), SIGNAL(excludedChanged()), m_signalMapper, SLOT(map()));
133     beginInsertRows(QModelIndex(), index, index);
134 }
135 
onPhraseAdded()136 void PhraseListModel::onPhraseAdded()
137 {
138     updateMappings();
139     endInsertRows();
140     emit countChanged();
141 }
142 
onPhraseAboutToBeRemoved(int index)143 void PhraseListModel::onPhraseAboutToBeRemoved(int index)
144 {
145     beginRemoveRows(QModelIndex(), index, index);
146 }
147 
onPhrasesRemoved()148 void PhraseListModel::onPhrasesRemoved()
149 {
150     endRemoveRows();
151     emit countChanged();
152 }
153 
emitPhraseChanged(int row)154 void PhraseListModel::emitPhraseChanged(int row)
155 {
156     beginResetModel();
157     endResetModel();
158     // FIXME very inefficient, but workaround to force new filtering in phrasefiltermodel
159     //      to exclude possible new excluded phrases
160     emit phraseChanged(row);
161     emit dataChanged(index(row, 0), index(row, 0));
162 }
163 
headerData(int section,Qt::Orientation orientation,int role) const164 QVariant PhraseListModel::headerData(int section, Qt::Orientation orientation, int role) const
165 {
166     if (role != Qt::DisplayRole) {
167         return QVariant();
168     }
169     if (orientation == Qt::Vertical) {
170         return QVariant(section + 1);
171     }
172     return QVariant(i18nc("@title:column", "Phrase"));
173 }
174 
count() const175 int PhraseListModel::count() const
176 {
177     if (!m_unit) {
178         return 0;
179     }
180     return m_unit->phrases().count();
181 }
182 
updateMappings()183 void PhraseListModel::updateMappings()
184 {
185     if (!m_unit) {
186         return;
187     }
188     int phrases = m_unit->phrases().count();
189     for (int i = 0; i < phrases; ++i) {
190         m_signalMapper->setMapping(m_unit->phrases().at(i).get(), i);
191     }
192 }
193