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