1 /*
2     This file is part of the Okteta Kasten module, made within the KDE community.
3 
4     SPDX-FileCopyrightText: 2008 Friedrich W. H. Kossebau <kossebau@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8 
9 #include "statistictablemodel.hpp"
10 
11 // Okteta core
12 #include <Okteta/Character>
13 #include <Okteta/CharCodec>
14 #include <Okteta/ValueCodec>
15 // KF
16 #include <KLocalizedString>
17 #include <KColorScheme>
18 // Qt
19 #include <QApplication>
20 
21 namespace Kasten {
22 
23 static constexpr QChar StatisticsDefaultSubstituteChar =  QLatin1Char('.');
24 static constexpr QChar StatisticsDefaultUndefinedChar =   QChar(QChar::ReplacementCharacter);
25 static constexpr Okteta::ValueCoding DefaultValueCoding =  Okteta::HexadecimalCoding;
26 static constexpr int StatisticsByteSetSize = 256;
27 
StatisticTableModel(int * byteCount,QObject * parent)28 StatisticTableModel::StatisticTableModel(int* byteCount, QObject* parent)
29     : QAbstractTableModel(parent)
30     , mByteCount(byteCount)
31     , mValueCoding(DefaultValueCoding)
32     , mValueCodec(Okteta::ValueCodec::createCodec(DefaultValueCoding))
33     , mCharCodec(Okteta::CharCodec::createCodec(Okteta::LocalEncoding))
34     , mSubstituteChar(StatisticsDefaultSubstituteChar)
35     , mUndefinedChar(StatisticsDefaultUndefinedChar)
36 {
37 }
38 
~StatisticTableModel()39 StatisticTableModel::~StatisticTableModel()
40 {
41     delete mValueCodec;
42     delete mCharCodec;
43 }
44 
update(int size)45 void StatisticTableModel::update(int size)
46 {
47     mSize = size;
48     emit dataChanged(index(0, CountId), index(StatisticsByteSetSize - 1, PercentId));
49     emit sizeChanged(mSize);
50 }
51 
setSubstituteChar(QChar substituteChar)52 void StatisticTableModel::setSubstituteChar(QChar substituteChar)
53 {
54     if (substituteChar.isNull()) {
55         substituteChar = StatisticsDefaultSubstituteChar;
56     }
57 
58     if (mSubstituteChar == substituteChar) {
59         return;
60     }
61 
62     mSubstituteChar = substituteChar;
63 
64     emit dataChanged(index(0, CharacterId), index(StatisticsByteSetSize - 1, CharacterId));
65 }
66 
setUndefinedChar(QChar undefinedChar)67 void StatisticTableModel::setUndefinedChar(QChar undefinedChar)
68 {
69     if (undefinedChar.isNull()) {
70         undefinedChar = StatisticsDefaultUndefinedChar;
71     }
72 
73     if (mUndefinedChar == undefinedChar) {
74         return;
75     }
76 
77     mUndefinedChar = undefinedChar;
78 
79     emit dataChanged(index(0, CharacterId), index(StatisticsByteSetSize - 1, CharacterId));
80 }
81 
setValueCoding(int valueCoding)82 void StatisticTableModel::setValueCoding(int valueCoding)
83 {
84     // no changes?
85     if (mValueCoding == valueCoding) {
86         return;
87     }
88 
89     delete mValueCodec;
90 
91     mValueCoding = (Okteta::ValueCoding)valueCoding;
92     mValueCodec = Okteta::ValueCodec::createCodec(mValueCoding);
93 //     CodedByte.resize( ByteCodec->encodingWidth() );
94 
95     emit dataChanged(index(0, ValueId), index(StatisticsByteSetSize - 1, ValueId));
96     emit headerChanged();
97 }
98 
setCharCodec(const QString & codeName)99 void StatisticTableModel::setCharCodec(const QString& codeName)
100 {
101     if (codeName == mCharCodec->name()) {
102         return;
103     }
104 
105     delete mCharCodec;
106     mCharCodec = Okteta::CharCodec::createCodec(codeName);
107 
108     emit dataChanged(index(0, CharacterId), index(StatisticsByteSetSize - 1, CharacterId));
109 }
110 
rowCount(const QModelIndex & parent) const111 int StatisticTableModel::rowCount(const QModelIndex& parent) const
112 {
113     return (!parent.isValid()) ? StatisticsByteSetSize : 0;
114 }
115 
columnCount(const QModelIndex & parent) const116 int StatisticTableModel::columnCount(const QModelIndex& parent) const
117 {
118     return (!parent.isValid()) ? NoOfIds : 0;
119 }
120 
data(const QModelIndex & index,int role) const121 QVariant StatisticTableModel::data(const QModelIndex& index, int role) const
122 {
123     QVariant result;
124     if (role == Qt::DisplayRole) {
125         const unsigned char byte = index.row();
126         const int column = index.column();
127         switch (column)
128         {
129         case CharacterId:
130         {
131             const Okteta::Character decodedChar = mCharCodec->decode(byte);
132             result =
133                 decodedChar.isUndefined() ?
134                     i18nc("@item:intable character is not defined", "undef.") :
135                 !decodedChar.isPrint() ?
136                     QString(mSubstituteChar) :
137                     QString(static_cast<QChar>(decodedChar));
138             // TODO: show proper descriptions for all control values, incl. space and delete
139             // cmp. KCharSelect
140             break;
141         }
142         case ValueId:
143         {
144             QString value;
145             mValueCodec->encode(&value, 0, byte);
146             result = value;
147             break;
148         }
149         case CountId:
150             result =  (mSize == -1) ?
151                      QVariant(QStringLiteral("-")) :
152                      QVariant(mByteCount[byte]);
153             break;
154         case PercentId:
155             result = (mSize > 0) ?
156                      // TODO: before we printed only a string (which killed sorting) with QString::number( x, 'f', 6 )
157                      // Qt now cuts trailing 0s, results in unaligned numbers, not so beautiful.
158                      QVariant(100.0 * (double)mByteCount[byte] / mSize) :
159                      QVariant(QStringLiteral("-"));
160             break;
161         default:
162             ;
163         }
164     } else if (role == Qt::TextAlignmentRole) {
165         result = Qt::AlignRight;
166     } else if (role == Qt::ForegroundRole) {
167         const int column = index.column();
168         bool isInactive = false;
169         switch (column)
170         {
171         case CharacterId:
172         {
173             const unsigned char byte = index.row();
174             const Okteta::Character decodedChar = mCharCodec->decode(byte);
175             isInactive = decodedChar.isUndefined() || !decodedChar.isPrint();
176             break;
177         }
178         case CountId:
179             isInactive = (mSize == -1);
180             break;
181         case PercentId:
182             isInactive = (mSize <= 0);
183             break;
184         default:
185             ;
186         }
187         if (isInactive) {
188             const QPalette& palette = QApplication::palette();
189             const KColorScheme colorScheme(palette.currentColorGroup(), KColorScheme::View);
190             result = colorScheme.foreground(KColorScheme::InactiveText);
191         }
192     }
193 
194     return result;
195 }
196 
headerData(int section,Qt::Orientation orientation,int role) const197 QVariant StatisticTableModel::headerData(int section, Qt::Orientation orientation, int role) const
198 {
199     QVariant result;
200 
201     if (role == Qt::DisplayRole) {
202         const QString titel =
203             section == ValueId ? (
204                 mValueCoding == Okteta::HexadecimalCoding ? i18nc("@title:column short for Hexadecimal", "Hex") :
205                 mValueCoding == Okteta::DecimalCoding ?     i18nc("@title:column short for Decimal",    "Dec") :
206                 mValueCoding == Okteta::OctalCoding ?       i18nc("@title:column short for Octal",      "Oct") :
207                 mValueCoding == Okteta::BinaryCoding ?      i18nc("@title:column short for Binary",     "Bin") :
208                                                             QString()) :
209             section == CharacterId ?   i18nc("@title:column short for Character",      "Char") :
210             section == CountId ?       i18nc("@title:column count of characters",      "Count") :
211             section == PercentId ?     i18nc("@title:column Percent of byte in total", "Percent") :
212                                        QString();
213         result = titel;
214     } else if (role == Qt::ToolTipRole) {
215         const QString titel =
216             section == ValueId ? (
217                 mValueCoding == Okteta::HexadecimalCoding ?
218                     i18nc("@info:tooltip column contains the value in hexadecimal format", "Hexadecimal") :
219                 mValueCoding == Okteta::DecimalCoding ?
220                     i18nc("@info:tooltip column contains the value in decimal format",    "Decimal") :
221                 mValueCoding == Okteta::OctalCoding ?
222                     i18nc("@info:tooltip column contains the value in octal format",      "Octal") :
223                 mValueCoding == Okteta::BinaryCoding ?
224                     i18nc("@info:tooltip column contains the value in binary format",     "Binary") :
225                 // else
226                     QString()) :
227             section == CharacterId ?
228                 i18nc("@info:tooltip column contains the character with the value",   "Character") :
229 //             section == CountId ?       i18nc("@info:tooltip count of characters",      "Count") :
230 //             section == PercentId ?     i18nc("@info:tooltip Percent of byte in total", "Percent") :
231                 QString();
232         result = titel;
233     } else {
234         result = QAbstractTableModel::headerData(section, orientation, role);
235     }
236 
237     return result;
238 }
239 
240 }
241