1 /*
2 This file is part of the Okteta Kasten module, made within the KDE community.
3
4 SPDX-FileCopyrightText: 2003, 2008-2009 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 "bytearrayframerenderer.hpp"
10
11 // lib
12 #include "printcolumnstylist.hpp"
13 // Okteta gui
14 #include <Okteta/OffsetColumnRenderer>
15 #include <Okteta/BorderColumnRenderer>
16 #include <Okteta/ValueByteArrayColumnRenderer>
17 #include <Okteta/CharByteArrayColumnRenderer>
18 #include <Okteta/ByteArrayTableLayout>
19 #include <Okteta/ByteArrayTableRanges>
20 // Okteta core
21 #include <Okteta/AbstractByteArrayModel>
22 #include <Okteta/ValueCodec>
23 #include <Okteta/CharCodec>
24 // Qt
25 #include <QHash>
26 #include <QDateTime>
27 #include <QPainter>
28 #include <QFontMetrics>
29 #include <QFontDatabase>
30 #include <QApplication>
31
32 static constexpr Okteta::Address DefaultStartOffset = 0;
33 static constexpr Okteta::Address DefaultFirstLineOffset = 0;
34 static constexpr int DefaultNoOfBytesPerLine = 16;
35 static constexpr LayoutStyle DefaultResizeStyle = FixedLayoutStyle; // krazy:exclude=staticobjects
36 static constexpr Okteta::ValueCoding DefaultValueCoding = Okteta::HexadecimalCoding; // krazy:exclude=staticobjects
37 static constexpr Okteta::CharCoding DefaultCharCoding = Okteta::LocalEncoding; // krazy:exclude=staticobjects
38
39 static constexpr int BAFInitialHeight = 50;
40 static constexpr int BAFInitialWidth = 50;
41
ByteArrayFrameRenderer()42 ByteArrayFrameRenderer::ByteArrayFrameRenderer()
43 : mHeight(BAFInitialHeight)
44 , mWidth(BAFInitialWidth)
45 , mResizeStyle(DefaultResizeStyle)
46 {
47 mLayout = new Okteta::ByteArrayTableLayout(DefaultNoOfBytesPerLine, DefaultFirstLineOffset, DefaultStartOffset, 0, 0);
48 mLayout->setNoOfLinesPerPage(noOfLinesPerFrame());
49 mTableRanges = new Okteta::ByteArrayTableRanges(mLayout);
50
51 // set codecs
52 mValueCodec = Okteta::ValueCodec::createCodec((Okteta::ValueCoding)DefaultValueCoding);
53 mValueCoding = DefaultValueCoding;
54 mCharCodec = Okteta::CharCodec::createCodec((Okteta::CharCoding)DefaultCharCoding);
55 mCharCoding = DefaultCharCoding;
56
57 mStylist = new Okteta::PrintColumnStylist();
58
59 // creating the columns in the needed order
60 mOffsetColumnRenderer =
61 new Okteta::OffsetColumnRenderer(mStylist, mLayout, Okteta::OffsetFormat::Hexadecimal);
62 mFirstBorderColumnRenderer =
63 new Okteta::BorderColumnRenderer(mStylist, true, false);
64 mValueColumnRenderer =
65 new Okteta::ValueByteArrayColumnRenderer(mStylist, mByteArrayModel, mLayout, mTableRanges);
66 mSecondBorderColumnRenderer =
67 new Okteta::BorderColumnRenderer(mStylist, true, false);
68 mCharColumnRenderer =
69 new Okteta::CharByteArrayColumnRenderer(mStylist, mByteArrayModel, mLayout, mTableRanges);
70
71 addColumn(mOffsetColumnRenderer);
72 addColumn(mFirstBorderColumnRenderer);
73 addColumn(mValueColumnRenderer);
74 addColumn(mSecondBorderColumnRenderer);
75 addColumn(mCharColumnRenderer);
76
77 mValueColumnRenderer->setValueCodec((Okteta::ValueCoding)mValueCoding, mValueCodec);
78 mValueColumnRenderer->setCharCodec(mCharCodec);
79 mCharColumnRenderer->setCharCodec(mCharCodec);
80
81 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
82 }
83
~ByteArrayFrameRenderer()84 ByteArrayFrameRenderer::~ByteArrayFrameRenderer()
85 {
86 delete mStylist;
87 delete mTableRanges;
88 delete mLayout;
89 delete mValueCodec;
90 delete mCharCodec;
91 }
92
byteArrayModel() const93 Okteta::AbstractByteArrayModel* ByteArrayFrameRenderer::byteArrayModel() const { return mByteArrayModel; }
offset() const94 Okteta::Address ByteArrayFrameRenderer::offset() const { return mLayout->startOffset(); }
length() const95 Okteta::Size ByteArrayFrameRenderer::length() const { return mLayout->length(); }
96
noOfBytesPerLine() const97 int ByteArrayFrameRenderer::noOfBytesPerLine() const { return mLayout->noOfBytesPerLine(); }
firstLineOffset() const98 Okteta::Address ByteArrayFrameRenderer::firstLineOffset() const { return mLayout->firstLineOffset(); }
startOffset() const99 Okteta::Address ByteArrayFrameRenderer::startOffset() const { return mLayout->startOffset(); }
layoutStyle() const100 LayoutStyle ByteArrayFrameRenderer::layoutStyle() const { return mResizeStyle; }
valueCoding() const101 Okteta::ValueCoding ByteArrayFrameRenderer::valueCoding() const { return mValueCoding; }
byteSpacingWidth() const102 Okteta::PixelX ByteArrayFrameRenderer::byteSpacingWidth() const { return mValueColumnRenderer->byteSpacingWidth(); }
noOfGroupedBytes() const103 int ByteArrayFrameRenderer::noOfGroupedBytes() const { return mValueColumnRenderer->noOfGroupedBytes(); }
groupSpacingWidth() const104 Okteta::PixelX ByteArrayFrameRenderer::groupSpacingWidth() const { return mValueColumnRenderer->groupSpacingWidth(); }
binaryGapWidth() const105 Okteta::PixelX ByteArrayFrameRenderer::binaryGapWidth() const { return mValueColumnRenderer->binaryGapWidth(); }
showsNonprinting() const106 bool ByteArrayFrameRenderer::showsNonprinting() const { return mCharColumnRenderer->isShowingNonprinting(); }
substituteChar() const107 QChar ByteArrayFrameRenderer::substituteChar() const { return mCharColumnRenderer->substituteChar(); }
undefinedChar() const108 QChar ByteArrayFrameRenderer::undefinedChar() const { return mCharColumnRenderer->undefinedChar(); }
charCoding() const109 Okteta::CharCoding ByteArrayFrameRenderer::charCoding() const { return mCharCoding; }
charCodingName() const110 const QString& ByteArrayFrameRenderer::charCodingName() const { return mCharCodec->name(); }
111
offsetColumnVisible() const112 bool ByteArrayFrameRenderer::offsetColumnVisible() const { return mOffsetColumnRenderer->isVisible(); }
visibleByteArrayCodings() const113 int ByteArrayFrameRenderer::visibleByteArrayCodings() const
114 { return (mValueColumnRenderer->isVisible() ? ValueCodingId : 0) | (mCharColumnRenderer->isVisible() ? CharCodingId : 0); }
115
height() const116 int ByteArrayFrameRenderer::height() const { return mHeight; }
width() const117 int ByteArrayFrameRenderer::width() const { return mWidth; }
118
framesCount() const119 int ByteArrayFrameRenderer::framesCount() const
120 {
121 const int charsPerFrame = mLayout->noOfBytesPerLine() * noOfLinesPerFrame();
122
123 // clever calculation works: at least one page for the rest
124 // hard to describe, think yourself
125 // TODO: needs to include the offset in the first line
126 const int frames = ((mLayout->length() - 1) / charsPerFrame) + 1;
127
128 return frames;
129 }
130
setByteArrayModel(Okteta::AbstractByteArrayModel * byteArrayModel,Okteta::Address offset,Okteta::Size length)131 void ByteArrayFrameRenderer::setByteArrayModel(Okteta::AbstractByteArrayModel* byteArrayModel,
132 Okteta::Address offset, Okteta::Size length)
133 {
134 mByteArrayModel = byteArrayModel;
135 length = (!byteArrayModel) ? 0 :
136 (length == -1) ? byteArrayModel->size() - offset :
137 (length <= byteArrayModel->size() - offset) ? length :
138 /* else */ byteArrayModel->size() - offset;
139
140 mValueColumnRenderer->set(byteArrayModel);
141 mCharColumnRenderer->set(byteArrayModel);
142
143 // affected:
144 // length -> no of lines -> width
145 mLayout->setByteArrayOffset(offset);
146 mLayout->setLength(length);
147
148 adjustLayoutToSize();
149 }
150
setHeight(int height)151 void ByteArrayFrameRenderer::setHeight(int height) { mHeight = height; }
setWidth(int width)152 void ByteArrayFrameRenderer::setWidth(int width)
153 {
154 if (mWidth == width) {
155 return;
156 }
157
158 mWidth = width;
159
160 adjustToWidth();
161 }
162
setFirstLineOffset(Okteta::Address firstLineOffset)163 void ByteArrayFrameRenderer::setFirstLineOffset(Okteta::Address firstLineOffset)
164 {
165 if (!mLayout->setFirstLineOffset(firstLineOffset)) {
166 return;
167 }
168
169 // affects:
170 // the no of lines -> width
171 adjustLayoutToSize();
172 }
173
setStartOffset(Okteta::Address startOffset)174 void ByteArrayFrameRenderer::setStartOffset(Okteta::Address startOffset)
175 {
176 if (!mLayout->setStartOffset(startOffset)) {
177 return;
178 }
179
180 // affects:
181 // the no of lines -> width
182 adjustLayoutToSize();
183 }
184
setBufferSpacing(Okteta::PixelX byteSpacing,int noOfGroupedBytes,Okteta::PixelX groupSpacing)185 void ByteArrayFrameRenderer::setBufferSpacing(Okteta::PixelX byteSpacing, int noOfGroupedBytes, Okteta::PixelX groupSpacing)
186 {
187 if (!mValueColumnRenderer->setSpacing(byteSpacing, noOfGroupedBytes, groupSpacing)) {
188 return;
189 }
190
191 adjustToWidth();
192 }
193
setLayoutStyle(LayoutStyle style)194 void ByteArrayFrameRenderer::setLayoutStyle(LayoutStyle style)
195 {
196 if (mResizeStyle == style) {
197 return;
198 }
199
200 mResizeStyle = style;
201
202 adjustToWidth();
203 }
204
setNoOfBytesPerLine(int noOfBytesPerLine)205 void ByteArrayFrameRenderer::setNoOfBytesPerLine(int noOfBytesPerLine)
206 {
207 // if the number is explicitly set we expect a wish for no automatic resize
208 mResizeStyle = FixedLayoutStyle;
209
210 if (!mLayout->setNoOfBytesPerLine(noOfBytesPerLine)) {
211 return;
212 }
213 adjustToWidth();
214 }
215
setByteSpacingWidth(Okteta::PixelX byteSpacingWidth)216 void ByteArrayFrameRenderer::setByteSpacingWidth(Okteta::PixelX byteSpacingWidth)
217 {
218 if (!mValueColumnRenderer->setByteSpacingWidth(byteSpacingWidth)) {
219 return;
220 }
221 adjustToWidth();
222 }
223
setNoOfGroupedBytes(int noOfGroupedBytes)224 void ByteArrayFrameRenderer::setNoOfGroupedBytes(int noOfGroupedBytes)
225 {
226 if (!mValueColumnRenderer->setNoOfGroupedBytes(noOfGroupedBytes)) {
227 return;
228 }
229 adjustToWidth();
230 }
231
setGroupSpacingWidth(Okteta::PixelX groupSpacingWidth)232 void ByteArrayFrameRenderer::setGroupSpacingWidth(Okteta::PixelX groupSpacingWidth)
233 {
234 if (!mValueColumnRenderer->setGroupSpacingWidth(groupSpacingWidth)) {
235 return;
236 }
237 adjustToWidth();
238 }
239
setBinaryGapWidth(Okteta::PixelX binaryGapWidth)240 void ByteArrayFrameRenderer::setBinaryGapWidth(Okteta::PixelX binaryGapWidth)
241 {
242 if (!mValueColumnRenderer->setBinaryGapWidth(binaryGapWidth)) {
243 return;
244 }
245 adjustToWidth();
246 }
247
setSubstituteChar(QChar substituteChar)248 void ByteArrayFrameRenderer::setSubstituteChar(QChar substituteChar)
249 {
250 mCharColumnRenderer->setSubstituteChar(substituteChar);
251 }
252
setUndefinedChar(QChar undefinedChar)253 void ByteArrayFrameRenderer::setUndefinedChar(QChar undefinedChar)
254 {
255 mCharColumnRenderer->setUndefinedChar(undefinedChar);
256 }
257
setShowsNonprinting(bool showsNonprinting)258 void ByteArrayFrameRenderer::setShowsNonprinting(bool showsNonprinting)
259 {
260 mCharColumnRenderer->setShowingNonprinting(showsNonprinting);
261 }
262
setValueCoding(Okteta::ValueCoding valueCoding)263 void ByteArrayFrameRenderer::setValueCoding(Okteta::ValueCoding valueCoding)
264 {
265 if (mValueCoding == valueCoding) {
266 return;
267 }
268
269 const uint oldCodingWidth = mValueCodec->encodingWidth();
270
271 Okteta::ValueCodec* newValueCodec =
272 Okteta::ValueCodec::createCodec(valueCoding);
273 if (!newValueCodec) {
274 return;
275 }
276
277 delete mValueCodec;
278 mValueCodec = newValueCodec;
279 mValueCoding = valueCoding;
280
281 mValueColumnRenderer->setValueCodec((Okteta::ValueCoding)mValueCoding, mValueCodec);
282
283 const uint newCodingWidth = mValueCodec->encodingWidth();
284
285 // change in the width?
286 if (newCodingWidth != oldCodingWidth) {
287 adjustToWidth();
288 }
289 }
290
setCharCoding(Okteta::CharCoding charCoding)291 void ByteArrayFrameRenderer::setCharCoding(Okteta::CharCoding charCoding)
292 {
293 if (mCharCoding == charCoding) {
294 return;
295 }
296
297 Okteta::CharCodec* newCharCodec = Okteta::CharCodec::createCodec(charCoding);
298 if (!newCharCodec) {
299 return;
300 }
301
302 delete mCharCodec;
303 mCharCodec = newCharCodec;
304 mCharCoding = charCoding;
305
306 mValueColumnRenderer->setCharCodec(mCharCodec);
307 mCharColumnRenderer->setCharCodec(mCharCodec);
308 }
309
310 // TODO: join with function above!
setCharCoding(const QString & newCharCodingName)311 void ByteArrayFrameRenderer::setCharCoding(const QString& newCharCodingName)
312 {
313 if (charCodingName() == newCharCodingName) {
314 return;
315 }
316
317 Okteta::CharCodec* newCharCodec = Okteta::CharCodec::createCodec(newCharCodingName);
318 if (!newCharCodec) {
319 return;
320 }
321
322 delete mCharCodec;
323 mCharCodec = newCharCodec;
324 mCharCoding = Okteta::LocalEncoding; // TODO: add encoding no to every known codec
325
326 mValueColumnRenderer->setCharCodec(mCharCodec);
327 mCharColumnRenderer->setCharCodec(mCharCodec);
328 }
329
setFont(const QFont & font)330 void ByteArrayFrameRenderer::setFont(const QFont& font)
331 {
332 mFont = font;
333
334 // get new values
335 QFontMetrics fontMetrics(font);
336
337 setLineHeight(fontMetrics.height());
338
339 // update all dependent structures
340 mLayout->setNoOfLinesPerPage(noOfLinesPerFrame());
341
342 mOffsetColumnRenderer->setFontMetrics(fontMetrics);
343 mValueColumnRenderer->setFontMetrics(fontMetrics);
344 mCharColumnRenderer->setFontMetrics(fontMetrics);
345
346 adjustToWidth();
347 }
348
prepare()349 void ByteArrayFrameRenderer::prepare()
350 {
351 }
352
renderFrame(QPainter * painter,int frameIndex)353 void ByteArrayFrameRenderer::renderFrame(QPainter* painter, int frameIndex)
354 {
355 painter->setFont(mFont);
356 AbstractColumnFrameRenderer::renderFrame(painter, frameIndex);
357 }
358
adjustToWidth()359 void ByteArrayFrameRenderer::adjustToWidth()
360 {
361 adjustToLayoutNoOfBytesPerLine();
362 adjustLayoutToSize();
363 }
364
adjustLayoutToSize()365 void ByteArrayFrameRenderer::adjustLayoutToSize()
366 {
367 // check whether there is a change with the numbers of fitting bytes per line
368 if (mResizeStyle != FixedLayoutStyle) {
369 const int bytesPerLine = fittingBytesPerLine();
370
371 // changes?
372 if (mLayout->setNoOfBytesPerLine(bytesPerLine)) {
373 adjustToLayoutNoOfBytesPerLine();
374 }
375 }
376
377 setNoOfLines(mLayout->noOfLines());
378 }
379
adjustToLayoutNoOfBytesPerLine()380 void ByteArrayFrameRenderer::adjustToLayoutNoOfBytesPerLine()
381 {
382 mValueColumnRenderer->resetXBuffer();
383 mCharColumnRenderer->resetXBuffer();
384
385 updateWidths();
386 }
387
showOffsetColumn(bool visible)388 void ByteArrayFrameRenderer::showOffsetColumn(bool visible)
389 {
390 bool OCVisible = mOffsetColumnRenderer->isVisible();
391 // no change?
392 if (OCVisible == visible) {
393 return;
394 }
395
396 mOffsetColumnRenderer->setVisible(visible);
397 mFirstBorderColumnRenderer->setVisible(visible);
398
399 adjustToWidth();
400 }
401
402 #if 0
403 QSize ByteArrayFrameRenderer::sizeHint() const
404 {
405 return QSize(columnsWidth(), columnsHeight());
406 }
407
408 QSize ByteArrayFrameRenderer::minimumSizeHint() const
409 {
410 // TODO: better minimal width (visibility!)
411
412 const int width =
413 mOffsetColumnRenderer->visibleWidth()
414 + mFirstBorderColumnRenderer->visibleWidth()
415 + mSecondBorderColumnRenderer->visibleWidth()
416 + mValueColumnRenderer->byteWidth()
417 + mCharColumnRenderer->byteWidth();
418 const int height = lineHeight() * noOfLines();
419
420 return QSize(width, height);
421 }
422 #endif
423
fittingBytesPerLine() const424 int ByteArrayFrameRenderer::fittingBytesPerLine() const
425 {
426 const Okteta::PixelX nonDataWidth =
427 mOffsetColumnRenderer->visibleWidth()
428 + mFirstBorderColumnRenderer->visibleWidth()
429 + mSecondBorderColumnRenderer->visibleWidth();
430
431 // abstract offset and border columns width
432 const Okteta::PixelX maxDataWidth = width() - nonDataWidth;
433
434 // prepare needed values
435 const Okteta::PixelX charByteWidth = mCharColumnRenderer->isVisible() ? mCharColumnRenderer->digitWidth() : 0;
436 const Okteta::PixelX valueByteWidth = mValueColumnRenderer->isVisible() ? mValueColumnRenderer->byteWidth() : 0;
437 const Okteta::PixelX valueByteSpacingWidth = mValueColumnRenderer->isVisible() ? mValueColumnRenderer->byteSpacingWidth() : 0;
438 Okteta::PixelX valueByteGroupSpacingWidth;
439 int noOfGroupedBytes = mValueColumnRenderer->noOfGroupedBytes();
440 // no grouping?
441 if (noOfGroupedBytes == 0) {
442 // faking grouping by 1
443 noOfGroupedBytes = 1;
444 valueByteGroupSpacingWidth = 0;
445 } else {
446 valueByteGroupSpacingWidth = mValueColumnRenderer->isVisible() ? mValueColumnRenderer->groupSpacingWidth() : 0;
447 }
448
449 Okteta::PixelX valueByteGroupWidth = noOfGroupedBytes * valueByteWidth + (noOfGroupedBytes - 1) * valueByteSpacingWidth;
450 Okteta::PixelX charByteGroupWidth = noOfGroupedBytes * charByteWidth;
451 Okteta::PixelX charAndValueGroupWidth = (valueByteGroupWidth + valueByteGroupSpacingWidth) + charByteGroupWidth;
452
453 // calculate fitting groups per line
454
455 // the last value byte group does not need a group spacing behind, but it gets into the calculation.
456 // so we simply add one to the max width to match that
457 const int fittingGroupsPerLine = (maxDataWidth + valueByteGroupSpacingWidth)
458 / charAndValueGroupWidth;
459
460 // calculate the fitting bytes per line by groups
461 int fittingBytesPerLine = noOfGroupedBytes * fittingGroupsPerLine;
462
463 // groups can be split and not only full groups are requested?
464 if (noOfGroupedBytes > 1 && mResizeStyle == FullSizeLayoutStyle) {
465 const int leftDataWidth = maxDataWidth - fittingGroupsPerLine * charAndValueGroupWidth;
466
467 if (leftDataWidth > 0) {
468 const int charAndValueByteWidth = valueByteWidth + valueByteSpacingWidth + charByteWidth;
469 // the last value byte does not need a spacing behind, but it gets into the calculation.
470 // so we simply add one to the left width to match that
471 const int ungroupedBytes = (leftDataWidth + valueByteSpacingWidth)
472 / charAndValueByteWidth;
473 fittingBytesPerLine += ungroupedBytes;
474 }
475
476 // is there not even the space for a single byte?
477 // if( fittingBytesPerLine < 1 )
478 // ensure at least one byte per line
479 // fittingBytesPerLine = 1;
480 } else {
481 // is there not the space for a single group?
482 // if( fittingBytesPerLine < 1 )
483 // ensures at least one group
484 // fittingBytesPerLine = noOfGroupedBytes;
485 }
486
487 return fittingBytesPerLine;
488 }
489
showByteArrayColumns(int newColumns)490 void ByteArrayFrameRenderer::showByteArrayColumns(int newColumns)
491 {
492 int columns = visibleByteArrayCodings();
493
494 // no changes or no column selected?
495 if (newColumns == columns || !(newColumns & (ValueCodingId | CharCodingId))) {
496 return;
497 }
498
499 mValueColumnRenderer->setVisible(ValueCodingId & newColumns);
500 mCharColumnRenderer->setVisible(CharCodingId & newColumns);
501 mSecondBorderColumnRenderer->setVisible(newColumns == (ValueCodingId | CharCodingId));
502
503 adjustToWidth();
504 }
505