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 }