File indexing completed on 2024-06-23 05:48:53
0001 /* 0002 This file is part of the Okteta Kasten module, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2003, 2008-2009 Friedrich W. H. Kossebau <kossebau@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 #include "bytearrayframerenderer.hpp" 0010 0011 // lib 0012 #include "printcolumnstylist.hpp" 0013 // Okteta gui 0014 #include <Okteta/OffsetColumnRenderer> 0015 #include <Okteta/BorderColumnRenderer> 0016 #include <Okteta/ValueByteArrayColumnRenderer> 0017 #include <Okteta/CharByteArrayColumnRenderer> 0018 #include <Okteta/ByteArrayTableLayout> 0019 #include <Okteta/ByteArrayTableRanges> 0020 // Okteta core 0021 #include <Okteta/AbstractByteArrayModel> 0022 #include <Okteta/ValueCodec> 0023 #include <Okteta/CharCodec> 0024 // Qt 0025 #include <QHash> 0026 #include <QDateTime> 0027 #include <QPainter> 0028 #include <QFontMetrics> 0029 #include <QFontDatabase> 0030 #include <QApplication> 0031 0032 static constexpr Okteta::Address DefaultStartOffset = 0; 0033 static constexpr Okteta::Address DefaultFirstLineOffset = 0; 0034 static constexpr int DefaultNoOfBytesPerLine = 16; 0035 static constexpr LayoutStyle DefaultResizeStyle = FixedLayoutStyle; // krazy:exclude=staticobjects 0036 static constexpr Okteta::OffsetFormat::Format DefaultOffsetCoding = Okteta::OffsetFormat::Hexadecimal; // krazy:exclude=staticobjects 0037 static constexpr Okteta::ValueCoding DefaultValueCoding = Okteta::HexadecimalCoding; // krazy:exclude=staticobjects 0038 static constexpr Okteta::CharCoding DefaultCharCoding = Okteta::LocalEncoding; // krazy:exclude=staticobjects 0039 0040 static constexpr int BAFInitialHeight = 50; 0041 static constexpr int BAFInitialWidth = 50; 0042 0043 ByteArrayFrameRenderer::ByteArrayFrameRenderer() 0044 : mHeight(BAFInitialHeight) 0045 , mWidth(BAFInitialWidth) 0046 , mResizeStyle(DefaultResizeStyle) 0047 { 0048 mLayout = new Okteta::ByteArrayTableLayout(DefaultNoOfBytesPerLine, DefaultFirstLineOffset, DefaultStartOffset, 0, 0); 0049 mLayout->setNoOfLinesPerPage(noOfLinesPerFrame()); 0050 mTableRanges = new Okteta::ByteArrayTableRanges(mLayout); 0051 0052 // set codecs 0053 mValueCodec = Okteta::ValueCodec::createCodec((Okteta::ValueCoding)DefaultValueCoding); 0054 mValueCoding = DefaultValueCoding; 0055 mCharCodec = Okteta::CharCodec::createCodec((Okteta::CharCoding)DefaultCharCoding); 0056 mCharCoding = DefaultCharCoding; 0057 0058 mStylist = new Okteta::PrintColumnStylist(); 0059 0060 // creating the columns in the needed order 0061 mOffsetColumnRenderer = 0062 new Okteta::OffsetColumnRenderer(mStylist, mLayout, DefaultOffsetCoding); 0063 mFirstBorderColumnRenderer = 0064 new Okteta::BorderColumnRenderer(mStylist, true, false); 0065 mValueColumnRenderer = 0066 new Okteta::ValueByteArrayColumnRenderer(mStylist, mByteArrayModel, mLayout, mTableRanges); 0067 mSecondBorderColumnRenderer = 0068 new Okteta::BorderColumnRenderer(mStylist, true, false); 0069 mCharColumnRenderer = 0070 new Okteta::CharByteArrayColumnRenderer(mStylist, mByteArrayModel, mLayout, mTableRanges); 0071 0072 addColumn(mOffsetColumnRenderer); 0073 addColumn(mFirstBorderColumnRenderer); 0074 addColumn(mValueColumnRenderer); 0075 addColumn(mSecondBorderColumnRenderer); 0076 addColumn(mCharColumnRenderer); 0077 0078 mValueColumnRenderer->setValueCodec((Okteta::ValueCoding)mValueCoding, mValueCodec); 0079 mValueColumnRenderer->setCharCodec(mCharCodec); 0080 mCharColumnRenderer->setCharCodec(mCharCodec); 0081 0082 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0083 } 0084 0085 ByteArrayFrameRenderer::~ByteArrayFrameRenderer() 0086 { 0087 delete mStylist; 0088 delete mTableRanges; 0089 delete mLayout; 0090 delete mValueCodec; 0091 delete mCharCodec; 0092 } 0093 0094 Okteta::AbstractByteArrayModel* ByteArrayFrameRenderer::byteArrayModel() const { return mByteArrayModel; } 0095 Okteta::Address ByteArrayFrameRenderer::offset() const { return mLayout->startOffset(); } 0096 Okteta::Size ByteArrayFrameRenderer::length() const { return mLayout->length(); } 0097 0098 int ByteArrayFrameRenderer::noOfBytesPerLine() const { return mLayout->noOfBytesPerLine(); } 0099 Okteta::Address ByteArrayFrameRenderer::firstLineOffset() const { return mLayout->firstLineOffset(); } 0100 Okteta::Address ByteArrayFrameRenderer::startOffset() const { return mLayout->startOffset(); } 0101 LayoutStyle ByteArrayFrameRenderer::layoutStyle() const { return mResizeStyle; } 0102 Okteta::ValueCoding ByteArrayFrameRenderer::valueCoding() const { return mValueCoding; } 0103 Okteta::PixelX ByteArrayFrameRenderer::byteSpacingWidth() const { return mValueColumnRenderer->byteSpacingWidth(); } 0104 int ByteArrayFrameRenderer::noOfGroupedBytes() const { return mValueColumnRenderer->noOfGroupedBytes(); } 0105 Okteta::PixelX ByteArrayFrameRenderer::groupSpacingWidth() const { return mValueColumnRenderer->groupSpacingWidth(); } 0106 Okteta::PixelX ByteArrayFrameRenderer::binaryGapWidth() const { return mValueColumnRenderer->binaryGapWidth(); } 0107 bool ByteArrayFrameRenderer::showsNonprinting() const { return mCharColumnRenderer->isShowingNonprinting(); } 0108 QChar ByteArrayFrameRenderer::substituteChar() const { return mCharColumnRenderer->substituteChar(); } 0109 QChar ByteArrayFrameRenderer::undefinedChar() const { return mCharColumnRenderer->undefinedChar(); } 0110 Okteta::CharCoding ByteArrayFrameRenderer::charCoding() const { return mCharCoding; } 0111 QString ByteArrayFrameRenderer::charCodingName() const { return mCharCodec->name(); } 0112 0113 bool ByteArrayFrameRenderer::offsetColumnVisible() const { return mOffsetColumnRenderer->isVisible(); } 0114 Okteta::OffsetFormat::Format ByteArrayFrameRenderer::offsetCoding() const { return mOffsetColumnRenderer->format(); } 0115 0116 int ByteArrayFrameRenderer::visibleByteArrayCodings() const 0117 { return (mValueColumnRenderer->isVisible() ? ValueCodingId : 0) | (mCharColumnRenderer->isVisible() ? CharCodingId : 0); } 0118 0119 int ByteArrayFrameRenderer::height() const { return mHeight; } 0120 int ByteArrayFrameRenderer::width() const { return mWidth; } 0121 0122 int ByteArrayFrameRenderer::framesCount() const 0123 { 0124 const int charsPerFrame = mLayout->noOfBytesPerLine() * noOfLinesPerFrame(); 0125 0126 // clever calculation works: at least one page for the rest 0127 // hard to describe, think yourself 0128 // TODO: needs to include the offset in the first line 0129 const int frames = ((mLayout->length() - 1) / charsPerFrame) + 1; 0130 0131 return frames; 0132 } 0133 0134 void ByteArrayFrameRenderer::setByteArrayModel(Okteta::AbstractByteArrayModel* byteArrayModel, 0135 Okteta::Address offset, Okteta::Size length) 0136 { 0137 mByteArrayModel = byteArrayModel; 0138 length = (!byteArrayModel) ? 0 : 0139 (length == -1) ? byteArrayModel->size() - offset : 0140 (length <= byteArrayModel->size() - offset) ? length : 0141 /* else */ byteArrayModel->size() - offset; 0142 0143 mValueColumnRenderer->set(byteArrayModel); 0144 mCharColumnRenderer->set(byteArrayModel); 0145 0146 // affected: 0147 // length -> no of lines -> width 0148 mLayout->setByteArrayOffset(offset); 0149 mLayout->setLength(length); 0150 0151 adjustLayoutToSize(); 0152 } 0153 0154 void ByteArrayFrameRenderer::setHeight(int height) { mHeight = height; } 0155 void ByteArrayFrameRenderer::setWidth(int width) 0156 { 0157 if (mWidth == width) { 0158 return; 0159 } 0160 0161 mWidth = width; 0162 0163 adjustToWidth(); 0164 } 0165 0166 void ByteArrayFrameRenderer::setFirstLineOffset(Okteta::Address firstLineOffset) 0167 { 0168 if (!mLayout->setFirstLineOffset(firstLineOffset)) { 0169 return; 0170 } 0171 0172 // affects: 0173 // the no of lines -> width 0174 adjustLayoutToSize(); 0175 } 0176 0177 void ByteArrayFrameRenderer::setStartOffset(Okteta::Address startOffset) 0178 { 0179 if (!mLayout->setStartOffset(startOffset)) { 0180 return; 0181 } 0182 0183 // affects: 0184 // the no of lines -> width 0185 adjustLayoutToSize(); 0186 } 0187 0188 void ByteArrayFrameRenderer::setBufferSpacing(Okteta::PixelX byteSpacing, int noOfGroupedBytes, Okteta::PixelX groupSpacing) 0189 { 0190 if (!mValueColumnRenderer->setSpacing(byteSpacing, noOfGroupedBytes, groupSpacing)) { 0191 return; 0192 } 0193 0194 adjustToWidth(); 0195 } 0196 0197 void ByteArrayFrameRenderer::setLayoutStyle(LayoutStyle style) 0198 { 0199 if (mResizeStyle == style) { 0200 return; 0201 } 0202 0203 mResizeStyle = style; 0204 0205 adjustToWidth(); 0206 } 0207 0208 void ByteArrayFrameRenderer::setNoOfBytesPerLine(int noOfBytesPerLine) 0209 { 0210 // if the number is explicitly set we expect a wish for no automatic resize 0211 mResizeStyle = FixedLayoutStyle; 0212 0213 if (!mLayout->setNoOfBytesPerLine(noOfBytesPerLine)) { 0214 return; 0215 } 0216 adjustToWidth(); 0217 } 0218 0219 void ByteArrayFrameRenderer::setByteSpacingWidth(Okteta::PixelX byteSpacingWidth) 0220 { 0221 if (!mValueColumnRenderer->setByteSpacingWidth(byteSpacingWidth)) { 0222 return; 0223 } 0224 adjustToWidth(); 0225 } 0226 0227 void ByteArrayFrameRenderer::setNoOfGroupedBytes(int noOfGroupedBytes) 0228 { 0229 if (!mValueColumnRenderer->setNoOfGroupedBytes(noOfGroupedBytes)) { 0230 return; 0231 } 0232 adjustToWidth(); 0233 } 0234 0235 void ByteArrayFrameRenderer::setGroupSpacingWidth(Okteta::PixelX groupSpacingWidth) 0236 { 0237 if (!mValueColumnRenderer->setGroupSpacingWidth(groupSpacingWidth)) { 0238 return; 0239 } 0240 adjustToWidth(); 0241 } 0242 0243 void ByteArrayFrameRenderer::setBinaryGapWidth(Okteta::PixelX binaryGapWidth) 0244 { 0245 if (!mValueColumnRenderer->setBinaryGapWidth(binaryGapWidth)) { 0246 return; 0247 } 0248 adjustToWidth(); 0249 } 0250 0251 void ByteArrayFrameRenderer::setSubstituteChar(QChar substituteChar) 0252 { 0253 mCharColumnRenderer->setSubstituteChar(substituteChar); 0254 } 0255 0256 void ByteArrayFrameRenderer::setUndefinedChar(QChar undefinedChar) 0257 { 0258 mCharColumnRenderer->setUndefinedChar(undefinedChar); 0259 } 0260 0261 void ByteArrayFrameRenderer::setShowsNonprinting(bool showsNonprinting) 0262 { 0263 mCharColumnRenderer->setShowingNonprinting(showsNonprinting); 0264 } 0265 0266 void ByteArrayFrameRenderer::setValueCoding(Okteta::ValueCoding valueCoding) 0267 { 0268 if (mValueCoding == valueCoding) { 0269 return; 0270 } 0271 0272 const uint oldCodingWidth = mValueCodec->encodingWidth(); 0273 0274 Okteta::ValueCodec* newValueCodec = 0275 Okteta::ValueCodec::createCodec(valueCoding); 0276 if (!newValueCodec) { 0277 return; 0278 } 0279 0280 delete mValueCodec; 0281 mValueCodec = newValueCodec; 0282 mValueCoding = valueCoding; 0283 0284 mValueColumnRenderer->setValueCodec((Okteta::ValueCoding)mValueCoding, mValueCodec); 0285 0286 const uint newCodingWidth = mValueCodec->encodingWidth(); 0287 0288 // change in the width? 0289 if (newCodingWidth != oldCodingWidth) { 0290 adjustToWidth(); 0291 } 0292 } 0293 0294 void ByteArrayFrameRenderer::setCharCoding(Okteta::CharCoding charCoding) 0295 { 0296 if (mCharCoding == charCoding) { 0297 return; 0298 } 0299 0300 Okteta::CharCodec* newCharCodec = Okteta::CharCodec::createCodec(charCoding); 0301 if (!newCharCodec) { 0302 return; 0303 } 0304 0305 delete mCharCodec; 0306 mCharCodec = newCharCodec; 0307 mCharCoding = charCoding; 0308 0309 mValueColumnRenderer->setCharCodec(mCharCodec); 0310 mCharColumnRenderer->setCharCodec(mCharCodec); 0311 } 0312 0313 // TODO: join with function above! 0314 void ByteArrayFrameRenderer::setCharCoding(const QString& newCharCodingName) 0315 { 0316 if (charCodingName() == newCharCodingName) { 0317 return; 0318 } 0319 0320 Okteta::CharCodec* newCharCodec = Okteta::CharCodec::createCodec(newCharCodingName); 0321 if (!newCharCodec) { 0322 return; 0323 } 0324 0325 delete mCharCodec; 0326 mCharCodec = newCharCodec; 0327 mCharCoding = Okteta::LocalEncoding; // TODO: add encoding no to every known codec 0328 0329 mValueColumnRenderer->setCharCodec(mCharCodec); 0330 mCharColumnRenderer->setCharCodec(mCharCodec); 0331 } 0332 0333 void ByteArrayFrameRenderer::setFont(const QFont& font) 0334 { 0335 mFont = font; 0336 0337 // get new values 0338 QFontMetrics fontMetrics(font); 0339 0340 setLineHeight(fontMetrics.height()); 0341 0342 // update all dependent structures 0343 mLayout->setNoOfLinesPerPage(noOfLinesPerFrame()); 0344 0345 mOffsetColumnRenderer->setFontMetrics(fontMetrics); 0346 mValueColumnRenderer->setFontMetrics(fontMetrics); 0347 mCharColumnRenderer->setFontMetrics(fontMetrics); 0348 0349 adjustToWidth(); 0350 } 0351 0352 void ByteArrayFrameRenderer::prepare() 0353 { 0354 } 0355 0356 void ByteArrayFrameRenderer::renderFrame(QPainter* painter, int frameIndex) 0357 { 0358 painter->setFont(mFont); 0359 AbstractColumnFrameRenderer::renderFrame(painter, frameIndex); 0360 } 0361 0362 void ByteArrayFrameRenderer::adjustToWidth() 0363 { 0364 adjustToLayoutNoOfBytesPerLine(); 0365 adjustLayoutToSize(); 0366 } 0367 0368 void ByteArrayFrameRenderer::adjustLayoutToSize() 0369 { 0370 // check whether there is a change with the numbers of fitting bytes per line 0371 if (mResizeStyle != FixedLayoutStyle) { 0372 const int bytesPerLine = fittingBytesPerLine(); 0373 0374 // changes? 0375 if (mLayout->setNoOfBytesPerLine(bytesPerLine)) { 0376 adjustToLayoutNoOfBytesPerLine(); 0377 } 0378 } 0379 0380 setNoOfLines(mLayout->noOfLines()); 0381 } 0382 0383 void ByteArrayFrameRenderer::adjustToLayoutNoOfBytesPerLine() 0384 { 0385 mValueColumnRenderer->resetXBuffer(); 0386 mCharColumnRenderer->resetXBuffer(); 0387 0388 updateWidths(); 0389 } 0390 0391 void ByteArrayFrameRenderer::showOffsetColumn(bool visible) 0392 { 0393 bool OCVisible = mOffsetColumnRenderer->isVisible(); 0394 // no change? 0395 if (OCVisible == visible) { 0396 return; 0397 } 0398 0399 mOffsetColumnRenderer->setVisible(visible); 0400 mFirstBorderColumnRenderer->setVisible(visible); 0401 0402 adjustToWidth(); 0403 } 0404 0405 void ByteArrayFrameRenderer::setOffsetCoding(Okteta::OffsetFormat::Format offsetCoding) 0406 { 0407 const Okteta::OffsetFormat::Format currentFormat = mOffsetColumnRenderer->format(); 0408 // no change? 0409 if (currentFormat == offsetCoding) { 0410 return; 0411 } 0412 0413 mOffsetColumnRenderer->setFormat(offsetCoding, QFontMetrics(mFont)); 0414 0415 adjustToWidth(); 0416 } 0417 0418 #if 0 0419 QSize ByteArrayFrameRenderer::sizeHint() const 0420 { 0421 return QSize(columnsWidth(), columnsHeight()); 0422 } 0423 0424 QSize ByteArrayFrameRenderer::minimumSizeHint() const 0425 { 0426 // TODO: better minimal width (visibility!) 0427 0428 const int width = 0429 mOffsetColumnRenderer->visibleWidth() 0430 + mFirstBorderColumnRenderer->visibleWidth() 0431 + mSecondBorderColumnRenderer->visibleWidth() 0432 + mValueColumnRenderer->byteWidth() 0433 + mCharColumnRenderer->byteWidth(); 0434 const int height = lineHeight() * noOfLines(); 0435 0436 return QSize(width, height); 0437 } 0438 #endif 0439 0440 int ByteArrayFrameRenderer::fittingBytesPerLine() const 0441 { 0442 const Okteta::PixelX nonDataWidth = 0443 mOffsetColumnRenderer->visibleWidth() 0444 + mFirstBorderColumnRenderer->visibleWidth() 0445 + mSecondBorderColumnRenderer->visibleWidth(); 0446 0447 // abstract offset and border columns width 0448 const Okteta::PixelX maxDataWidth = width() - nonDataWidth; 0449 0450 // prepare needed values 0451 const Okteta::PixelX charByteWidth = mCharColumnRenderer->isVisible() ? mCharColumnRenderer->digitWidth() : 0; 0452 const Okteta::PixelX valueByteWidth = mValueColumnRenderer->isVisible() ? mValueColumnRenderer->byteWidth() : 0; 0453 const Okteta::PixelX valueByteSpacingWidth = mValueColumnRenderer->isVisible() ? mValueColumnRenderer->byteSpacingWidth() : 0; 0454 Okteta::PixelX valueByteGroupSpacingWidth; 0455 int noOfGroupedBytes = mValueColumnRenderer->noOfGroupedBytes(); 0456 // no grouping? 0457 if (noOfGroupedBytes == 0) { 0458 // faking grouping by 1 0459 noOfGroupedBytes = 1; 0460 valueByteGroupSpacingWidth = 0; 0461 } else { 0462 valueByteGroupSpacingWidth = mValueColumnRenderer->isVisible() ? mValueColumnRenderer->groupSpacingWidth() : 0; 0463 } 0464 0465 Okteta::PixelX valueByteGroupWidth = noOfGroupedBytes * valueByteWidth + (noOfGroupedBytes - 1) * valueByteSpacingWidth; 0466 Okteta::PixelX charByteGroupWidth = noOfGroupedBytes * charByteWidth; 0467 Okteta::PixelX charAndValueGroupWidth = (valueByteGroupWidth + valueByteGroupSpacingWidth) + charByteGroupWidth; 0468 0469 // calculate fitting groups per line 0470 0471 // the last value byte group does not need a group spacing behind, but it gets into the calculation. 0472 // so we simply add one to the max width to match that 0473 const int fittingGroupsPerLine = (maxDataWidth + valueByteGroupSpacingWidth) 0474 / charAndValueGroupWidth; 0475 0476 // calculate the fitting bytes per line by groups 0477 int fittingBytesPerLine = noOfGroupedBytes * fittingGroupsPerLine; 0478 0479 // groups can be split and not only full groups are requested? 0480 if (noOfGroupedBytes > 1 && mResizeStyle == FullSizeLayoutStyle) { 0481 const int leftDataWidth = maxDataWidth - fittingGroupsPerLine * charAndValueGroupWidth; 0482 0483 if (leftDataWidth > 0) { 0484 const int charAndValueByteWidth = valueByteWidth + valueByteSpacingWidth + charByteWidth; 0485 // the last value byte does not need a spacing behind, but it gets into the calculation. 0486 // so we simply add one to the left width to match that 0487 const int ungroupedBytes = (leftDataWidth + valueByteSpacingWidth) 0488 / charAndValueByteWidth; 0489 fittingBytesPerLine += ungroupedBytes; 0490 } 0491 0492 // is there not even the space for a single byte? 0493 // if( fittingBytesPerLine < 1 ) 0494 // ensure at least one byte per line 0495 // fittingBytesPerLine = 1; 0496 } else { 0497 // is there not the space for a single group? 0498 // if( fittingBytesPerLine < 1 ) 0499 // ensures at least one group 0500 // fittingBytesPerLine = noOfGroupedBytes; 0501 } 0502 0503 return fittingBytesPerLine; 0504 } 0505 0506 void ByteArrayFrameRenderer::showByteArrayColumns(int newColumns) 0507 { 0508 int columns = visibleByteArrayCodings(); 0509 0510 // no changes or no column selected? 0511 if (newColumns == columns || !(newColumns & (ValueCodingId | CharCodingId))) { 0512 return; 0513 } 0514 0515 mValueColumnRenderer->setVisible(ValueCodingId & newColumns); 0516 mCharColumnRenderer->setVisible(CharCodingId & newColumns); 0517 mSecondBorderColumnRenderer->setVisible(newColumns == (ValueCodingId | CharCodingId)); 0518 0519 adjustToWidth(); 0520 }