1 /***********************************************************************
2 *
3 * Copyright (C) 2012, 2013, 2014, 2016, 2018 Graeme Gott <graeme@gottcode.org>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 ***********************************************************************/
19
20 #include "symbols_model.h"
21
22 #include <QApplication>
23 #include <QBuffer>
24 #include <QDataStream>
25 #include <QFile>
26 #include <QFileInfo>
27 #include <QPalette>
28
29 #include <climits>
30
31 //-----------------------------------------------------------------------------
32
operator >>(QDataStream & stream,SymbolsModel::Filter::Range & range)33 QDataStream& operator>>(QDataStream& stream, SymbolsModel::Filter::Range& range)
34 {
35 stream >> range.start >> range.end;
36 return stream;
37 }
38
operator >>(QDataStream & stream,SymbolsModel::Filter & filter)39 QDataStream& operator>>(QDataStream& stream, SymbolsModel::Filter& filter)
40 {
41 stream >> filter.name >> filter.size >> filter.ranges;
42 return stream;
43 }
44
45 //-----------------------------------------------------------------------------
46
47 QString SymbolsModel::m_path;
48
49 //-----------------------------------------------------------------------------
50
SymbolsModel(QObject * parent)51 SymbolsModel::SymbolsModel(QObject* parent) :
52 QAbstractItemModel(parent)
53 {
54 QFile file(m_path);
55 if (!file.open(QFile::ReadOnly)) {
56 return;
57 }
58 QByteArray data = qUncompress(file.readAll());
59 file.close();
60
61 QBuffer buffer(&data);
62 if (!buffer.open(QBuffer::ReadOnly)) {
63 return;
64 }
65
66 QDataStream stream(&buffer);
67 stream.setVersion(QDataStream::Qt_5_2);
68 stream >> m_names;
69 stream >> m_groups;
70 buffer.close();
71 }
72
73 //-----------------------------------------------------------------------------
74
filters(int group) const75 QStringList SymbolsModel::filters(int group) const
76 {
77 // Find group
78 QStringList names;
79 if ((group < 0) || (group >= m_groups.count())) {
80 return names;
81 }
82 const FilterGroup& filters = m_groups.at(group);
83
84 // List names of all filters in group
85 for (const Filter& filter : filters) {
86 names += QLatin1String(filter.name);
87 }
88 return names;
89 }
90
91 //-----------------------------------------------------------------------------
92
filterGroups() const93 QStringList SymbolsModel::filterGroups() const
94 {
95 return QStringList() << tr("Blocks") << tr("Scripts");
96 }
97
98 //-----------------------------------------------------------------------------
99
setFilter(int group,int index)100 void SymbolsModel::setFilter(int group, int index)
101 {
102 // Find filter
103 if ((group < 0) || (group >= m_groups.count())) {
104 return;
105 }
106 const FilterGroup& filters = m_groups.at(group);
107 if ((index < 0) || (index >= filters.count())) {
108 return;
109 }
110 const Filter& filter = filters.at(index);
111
112 // Clear list of symbols
113 beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
114 m_symbols.resize(0);
115 endRemoveRows();
116
117 // Allocate space for symbols
118 int padding = filter.size % 16;
119 padding = padding ? (16 - padding) : 0;
120 int size = filter.size + padding;
121 if (m_symbols.capacity() < size) {
122 m_symbols.reserve(size);
123 }
124
125 // Create list of symbols
126 beginInsertRows(QModelIndex(), 0, (size / 16) - 1);
127 for (int i = 0, count = filter.ranges.count(); i < count; ++i) {
128 const Filter::Range& range = filter.ranges.at(i);
129 for (quint32 code = range.start; code <= range.end; ++code) {
130 m_symbols.append(code);
131 }
132 }
133
134 // Pad list of symbols to be multiple of 16
135 for (int i = 0; i < padding; ++i) {
136 m_symbols.append(UINT_MAX);
137 }
138 endInsertRows();
139 }
140
141 //-----------------------------------------------------------------------------
142
symbolFilter(int group,quint32 unicode) const143 int SymbolsModel::symbolFilter(int group, quint32 unicode) const
144 {
145 // Find group
146 int index = -1;
147 if ((group < 0) || (group >= m_groups.count())) {
148 return index;
149 }
150 const FilterGroup& filters = m_groups.at(group);
151
152 // Check for filter whose ranges contain symbol
153 for (int i = 0, count = filters.count(); i < count; ++i) {
154 const Filter& filter = filters.at(i);
155 for (const Filter::Range& range : filter.ranges) {
156 if ((range.start <= unicode) && (range.end >= unicode)) {
157 index = i;
158 break;
159 }
160 }
161 }
162 return index;
163 }
164
165 //-----------------------------------------------------------------------------
166
symbolName(quint32 unicode) const167 QString SymbolsModel::symbolName(quint32 unicode) const
168 {
169 if ((unicode >= 0x3400 && unicode <= 0x4DBF)
170 || (unicode >= 0x4E00 && unicode <= 0x9FFF)
171 || (unicode >= 0x20000 && unicode <= 0x2A6DF)
172 || (unicode >= 0x2A700 && unicode <= 0x2B81F)) {
173 return QLatin1String("CJK UNIFIED IDEOGRAPH-") + QString::number(unicode, 16).toUpper();
174 } else if (unicode >= 0xAC00 && unicode <= 0xD7AF) {
175 // Hangul character name algorithm from section 3.12 of Unicode standard
176 static const int SBase = 0xAC00;
177 static const int LCount = 19;
178 static const int VCount = 21;
179 static const int TCount = 28;
180 static const int NCount = (VCount * TCount);
181 static const int SCount = (LCount * NCount);
182
183 static const char JAMO_L_TABLE[][4] =
184 {
185 "G", "GG", "N", "D", "DD", "R", "M", "B", "BB",
186 "S", "SS", "", "J", "JJ", "C", "K", "T", "P", "H"
187 };
188
189 static const char JAMO_V_TABLE[][4] =
190 {
191 "A", "AE", "YA", "YAE", "EO", "E", "YEO", "YE", "O",
192 "WA", "WAE", "OE", "YO", "U", "WEO", "WE", "WI",
193 "YU", "EU", "YI", "I"
194 };
195
196 static const char JAMO_T_TABLE[][4] =
197 {
198 "", "G", "GG", "GS", "N", "NJ", "NH", "D", "L", "LG", "LM",
199 "LB", "LS", "LT", "LP", "LH", "M", "B", "BS",
200 "S", "SS", "NG", "J", "C", "K", "T", "P", "H"
201 };
202
203 int SIndex = unicode - SBase;
204 if (SIndex < 0 || SIndex >= SCount) {
205 return QString();
206 }
207
208 int LIndex = SIndex / NCount;
209 int VIndex = (SIndex % NCount) / TCount;
210 int TIndex = SIndex % TCount;
211
212 return QLatin1String("HANGUL SYLLABLE ") + QLatin1String(JAMO_L_TABLE[LIndex]) +
213 QLatin1String(JAMO_V_TABLE[VIndex]) + QLatin1String(JAMO_T_TABLE[TIndex]);
214 } else {
215 return QLatin1String(m_names[unicode]);
216 }
217 }
218
219 //-----------------------------------------------------------------------------
220
columnCount(const QModelIndex & parent) const221 int SymbolsModel::columnCount(const QModelIndex& parent) const
222 {
223 return !parent.isValid() ? 16 : 0;
224 }
225
226 //-----------------------------------------------------------------------------
227
data(const QModelIndex & index,int role) const228 QVariant SymbolsModel::data(const QModelIndex& index, int role) const
229 {
230 if (!index.isValid()) {
231 return QVariant();
232 }
233
234 quint32 unicode = index.internalId();
235 bool printable = QChar(unicode).isPrint();
236 switch (role) {
237 case Qt::BackgroundRole:
238 return printable ? QVariant() : QApplication::palette().button();
239 case Qt::DisplayRole:
240 return printable ? QString::fromUcs4(&unicode, 1) : QString();
241 case Qt::TextAlignmentRole:
242 return Qt::AlignCenter;
243 default:
244 return QVariant();
245 }
246 }
247
248 //-----------------------------------------------------------------------------
249
flags(const QModelIndex & index) const250 Qt::ItemFlags SymbolsModel::flags(const QModelIndex& index) const
251 {
252 if (!index.isValid() || (index.internalId() > 0x10FFFF)) {
253 return Qt::NoItemFlags;
254 }
255
256 return Qt::ItemIsSelectable|Qt::ItemIsEnabled;
257 }
258
259 //-----------------------------------------------------------------------------
260
index(quint32 unicode) const261 QModelIndex SymbolsModel::index(quint32 unicode) const
262 {
263 int index = m_symbols.indexOf(unicode);
264 if (index != -1) {
265 return createIndex(index / 16, index % 16, unicode);
266 } else {
267 return QModelIndex();
268 }
269 }
270
271 //-----------------------------------------------------------------------------
272
index(int row,int column,const QModelIndex & parent) const273 QModelIndex SymbolsModel::index(int row, int column, const QModelIndex& parent) const
274 {
275 int pos = (row * 16) + column;
276 return (!parent.isValid() && (pos < m_symbols.count())) ? createIndex(row, column, m_symbols.at(pos)) : QModelIndex();
277 }
278
279 //-----------------------------------------------------------------------------
280
parent(const QModelIndex &) const281 QModelIndex SymbolsModel::parent(const QModelIndex&) const
282 {
283 return QModelIndex();
284 }
285
286 //-----------------------------------------------------------------------------
287
rowCount(const QModelIndex & parent) const288 int SymbolsModel::rowCount(const QModelIndex& parent) const
289 {
290 return !parent.isValid() ? (m_symbols.count() / 16) : 0;
291 }
292
293 //-----------------------------------------------------------------------------
294
setData(const QStringList & datadirs)295 void SymbolsModel::setData(const QStringList& datadirs)
296 {
297 for (const QString& path : datadirs) {
298 QFileInfo info(path + "/symbols1000.dat");
299 if (info.exists()) {
300 m_path = info.absoluteFilePath();
301 break;
302 }
303 }
304 }
305
306 //-----------------------------------------------------------------------------
307