File indexing completed on 2024-04-21 05:53:08

0001 /*
0002     This file is part of the Okteta Gui library, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2008-2010 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 "bytearrayrowview_p.hpp"
0010 
0011 // lib
0012 #include "oktetagui.hpp"
0013 #include "bordercolumnrenderer.hpp"
0014 #include "widgetcolumnstylist.hpp"
0015 #include "controller/dropper.hpp"
0016 #include "cursor.hpp"
0017 #include <logging.hpp>
0018 // Okteta core
0019 #include <Okteta/ValueCodec>
0020 // Qt
0021 #include <QStyle>
0022 #include <QFontDatabase>
0023 #include <QPainter>
0024 #include <QScrollBar>
0025 #include <QEvent>
0026 // Std
0027 #include <utility>
0028 
0029 namespace Okteta {
0030 
0031 ByteArrayRowViewPrivate::ByteArrayRowViewPrivate(ByteArrayRowView* parent)
0032     : AbstractByteArrayViewPrivate(parent)
0033 {
0034 }
0035 
0036 ByteArrayRowViewPrivate::~ByteArrayRowViewPrivate() = default;
0037 
0038 void ByteArrayRowViewPrivate::init()
0039 {
0040     Q_Q(ByteArrayRowView);
0041 
0042     // creating the columns in the needed order
0043     mByteArrayColumn =
0044         new ByteArrayRowColumnRenderer(mStylist, mByteArrayModel, mTableLayout, mTableRanges);
0045 
0046     q->addColumn(mOffsetColumn);
0047     q->addColumn(mOffsetBorderColumn);
0048     q->addColumn(mByteArrayColumn);
0049 
0050     // select the active column
0051     mActiveCoding = AbstractByteArrayView::CharCodingId;
0052     mInactiveCoding = AbstractByteArrayView::ValueCodingId;
0053 
0054     // set char encoding
0055     mByteArrayColumn->setValueCodec((ValueCoding)mValueCoding, mValueCodec);
0056     mByteArrayColumn->setCharCodec(mCharCodec);
0057 //     mByteArrayColumn->setActiveCoding( mActiveCoding );
0058 
0059     adaptController();
0060 
0061     // do here, not in base class, as changeEvent(fontEvent) needs this init run before
0062     q->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
0063 }
0064 
0065 AbstractByteArrayView::CodingTypes ByteArrayRowViewPrivate::visibleCodings() const { return mByteArrayColumn->visibleCodings(); }
0066 ByteArrayRowView::CodingTypeId ByteArrayRowViewPrivate::activeCoding() const { return mActiveCoding; }
0067 
0068 void ByteArrayRowViewPrivate::setByteArrayModel(AbstractByteArrayModel* _byteArrayModel)
0069 {
0070     mValueEditor->reset();
0071 
0072     // TODO: this fails if _byteArrayModel == null
0073     mByteArrayColumn->set(_byteArrayModel);
0074 
0075     AbstractByteArrayViewPrivate::setByteArrayModel(_byteArrayModel);
0076 }
0077 
0078 void ByteArrayRowViewPrivate::setBufferSpacing(PixelX ByteSpacing, int noOfGroupedBytes, PixelX GroupSpacing)
0079 {
0080     if (!mByteArrayColumn->setSpacing(ByteSpacing, noOfGroupedBytes, GroupSpacing)) {
0081         return;
0082     }
0083 
0084     updateViewByWidth();
0085 }
0086 
0087 void ByteArrayRowViewPrivate::setValueCoding(AbstractByteArrayView::ValueCoding valueCoding)
0088 {
0089     Q_Q(ByteArrayRowView);
0090 
0091     if (mValueCoding == valueCoding) {
0092         return;
0093     }
0094 
0095     const uint oldCodingWidth = mValueCodec->encodingWidth();
0096 
0097     AbstractByteArrayViewPrivate::setValueCoding(valueCoding);
0098 
0099     mByteArrayColumn->setValueCodec((ValueCoding)mValueCoding, mValueCodec);
0100     mValueEditor->adaptToValueCodecChange();
0101 
0102     const uint newCodingWidth = mValueCodec->encodingWidth();
0103 
0104     // no change in the width?
0105     if (newCodingWidth == oldCodingWidth) {
0106         q->updateColumn(*mByteArrayColumn);
0107     } else {
0108         updateViewByWidth();
0109     }
0110 
0111     Q_EMIT q->valueCodingChanged(valueCoding);
0112 }
0113 
0114 void ByteArrayRowViewPrivate::setByteSpacingWidth(int /*PixelX*/ byteSpacingWidth)
0115 {
0116     if (!mByteArrayColumn->setByteSpacingWidth(byteSpacingWidth)) {
0117         return;
0118     }
0119     updateViewByWidth();
0120 }
0121 
0122 void ByteArrayRowViewPrivate::setNoOfGroupedBytes(int noOfGroupedBytes)
0123 {
0124     Q_Q(ByteArrayRowView);
0125 
0126     if (!mByteArrayColumn->setNoOfGroupedBytes(noOfGroupedBytes)) {
0127         return;
0128     }
0129     updateViewByWidth();
0130 
0131     Q_EMIT q->noOfGroupedBytesChanged(noOfGroupedBytes);
0132 }
0133 
0134 void ByteArrayRowViewPrivate::setGroupSpacingWidth(int /*PixelX*/ groupSpacingWidth)
0135 {
0136     if (!mByteArrayColumn->setGroupSpacingWidth(groupSpacingWidth)) {
0137         return;
0138     }
0139     updateViewByWidth();
0140 }
0141 
0142 void ByteArrayRowViewPrivate::setBinaryGapWidth(int /*PixelX*/ binaryGapWidth)
0143 {
0144     if (!mByteArrayColumn->setBinaryGapWidth(binaryGapWidth)) {
0145         return;
0146     }
0147     updateViewByWidth();
0148 }
0149 
0150 void ByteArrayRowViewPrivate::setSubstituteChar(QChar substituteChar)
0151 {
0152     Q_Q(ByteArrayRowView);
0153 
0154     if (!mByteArrayColumn->setSubstituteChar(substituteChar)) {
0155         return;
0156     }
0157     pauseCursor();
0158     q->updateColumn(*mByteArrayColumn);
0159     unpauseCursor();
0160 
0161     Q_EMIT q->substituteCharChanged(substituteChar);
0162 }
0163 
0164 void ByteArrayRowViewPrivate::setUndefinedChar(QChar undefinedChar)
0165 {
0166     Q_Q(ByteArrayRowView);
0167 
0168     if (!mByteArrayColumn->setUndefinedChar(undefinedChar)) {
0169         return;
0170     }
0171     pauseCursor();
0172     q->updateColumn(*mByteArrayColumn);
0173     unpauseCursor();
0174 
0175     Q_EMIT q->undefinedCharChanged(undefinedChar);
0176 }
0177 
0178 void ByteArrayRowViewPrivate::setShowsNonprinting(bool showsNonprinting)
0179 {
0180     Q_Q(ByteArrayRowView);
0181 
0182     if (!mByteArrayColumn->setShowingNonprinting(showsNonprinting)) {
0183         return;
0184     }
0185     pauseCursor();
0186     q->updateColumn(*mByteArrayColumn);
0187     unpauseCursor();
0188 
0189     Q_EMIT q->showsNonprintingChanged(showsNonprinting);
0190 }
0191 
0192 void ByteArrayRowViewPrivate::setCharCoding(AbstractByteArrayView::CharCoding charCoding)
0193 {
0194     Q_Q(ByteArrayRowView);
0195 
0196     if (mCharCoding == charCoding) {
0197         return;
0198     }
0199 
0200     AbstractByteArrayViewPrivate::setCharCoding(charCoding);
0201 
0202     pauseCursor();
0203 
0204     mByteArrayColumn->setCharCodec(mCharCodec);
0205 
0206     q->updateColumn(*mByteArrayColumn);
0207 
0208     unpauseCursor();
0209 
0210     Q_EMIT q->charCodecChanged(charCodingName());
0211 }
0212 
0213 // TODO: join with function above!
0214 void ByteArrayRowViewPrivate::setCharCoding(const QString& newCharCodingName)
0215 {
0216     Q_Q(ByteArrayRowView);
0217 
0218     if (charCodingName() == newCharCodingName) {
0219         return;
0220     }
0221 
0222     AbstractByteArrayViewPrivate::setCharCoding(newCharCodingName);
0223 
0224     pauseCursor();
0225 
0226     mByteArrayColumn->setCharCodec(mCharCodec);
0227 
0228     q->updateColumn(*mByteArrayColumn);
0229 
0230     unpauseCursor();
0231 
0232     Q_EMIT q->charCodecChanged(charCodingName());
0233 }
0234 
0235 void ByteArrayRowViewPrivate::setByteTypeColored(bool isColored)
0236 {
0237     Q_Q(ByteArrayRowView);
0238 
0239     if (isColored == mByteArrayColumn->isByteTypeColored()) {
0240         return;
0241     }
0242 
0243     mByteArrayColumn->setByteTypeColored(isColored);
0244 
0245     pauseCursor();
0246     q->updateColumn(*mByteArrayColumn);
0247     unpauseCursor();
0248 }
0249 
0250 void ByteArrayRowViewPrivate::changeEvent(QEvent* event)
0251 {
0252     Q_Q(ByteArrayRowView);
0253 
0254     // make sure base class deals with this event first,
0255     // as some values which are used here are updated there
0256     q->AbstractByteArrayView::changeEvent(event);
0257 
0258     if (event->type() != QEvent::FontChange) {
0259         return;
0260     }
0261 
0262     // get new values
0263     const QFontMetrics newFontMetrics = q->fontMetrics();
0264 
0265     mOffsetColumn->setFontMetrics(newFontMetrics);
0266     mByteArrayColumn->setFontMetrics(newFontMetrics);
0267 
0268     const int rowHeight = mByteArrayColumn->rowHeight();
0269 
0270     q->setLineHeight(rowHeight);
0271 
0272     // update all dependent structures
0273     mTableLayout->setNoOfLinesPerPage(q->noOfLinesPerPage());
0274 
0275     updateViewByWidth();
0276 }
0277 
0278 void ByteArrayRowViewPrivate::adjustToLayoutNoOfBytesPerLine()
0279 {
0280     Q_Q(ByteArrayRowView);
0281 
0282     mByteArrayColumn->resetXBuffer();
0283 
0284     q->updateWidths();
0285 }
0286 
0287 QSize ByteArrayRowViewPrivate::minimumSizeHint() const
0288 {
0289     Q_Q(const ByteArrayRowView);
0290 
0291     // TODO: better minimal width (visibility!)
0292     const int minWidth =
0293         mOffsetColumn->visibleWidth()
0294         + mOffsetBorderColumn->visibleWidth()
0295         + mByteArrayColumn->byteWidth();
0296     const int minHeight =
0297         q->lineHeight()
0298         + q->noOfLines() > 1 ? q->style()->pixelMetric(QStyle::PM_ScrollBarExtent) : 0;
0299 
0300     return {qMin(minWidth, 100), qMin(minHeight, 100)};
0301 }
0302 
0303 int ByteArrayRowViewPrivate::fittingBytesPerLine() const
0304 {
0305     Q_Q(const ByteArrayRowView);
0306 
0307     const QSize newSize = q->maximumViewportSize();
0308     const PixelX reservedWidth =
0309         mOffsetColumn->visibleWidth()
0310         + mOffsetBorderColumn->visibleWidth();
0311 
0312     // abstract offset and border columns width
0313     const PixelX fullWidth = newSize.width() - reservedWidth;
0314 
0315     //  // no width left for resizeable columns? TODO: put this in resizeEvent
0316     //  if( fullWidth < 0 )
0317     //    return;
0318 
0319     const PixelY fullHeight = newSize.height();
0320 
0321     // check influence of dis-/appearing of the vertical scrollbar
0322     const bool verticalScrollbarIsVisible = q->verticalScrollBar()->isVisible();
0323     const PixelX scrollbarExtent = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
0324 
0325     PixelX availableWidth = fullWidth;
0326     if (verticalScrollbarIsVisible) {
0327         availableWidth -= scrollbarExtent;
0328     }
0329 
0330     enum MatchTrial
0331     {
0332         FirstRun,
0333         RerunWithScrollbarOn,
0334         TestWithoutScrollbar
0335     };
0336     MatchTrial matchRun = FirstRun;
0337 
0338     // prepare needed values
0339     const PixelX byteWidth = mByteArrayColumn->byteWidth();
0340     const PixelX byteSpacingWidth = mByteArrayColumn->byteSpacingWidth();
0341     PixelX groupSpacingWidth;
0342     int noOfGroupedBytes = mByteArrayColumn->noOfGroupedBytes();
0343     // no grouping?
0344     if (noOfGroupedBytes == 0) {
0345         // fake no grouping by grouping with 1 and using byteSpacingWidth
0346         noOfGroupedBytes = 1;
0347         groupSpacingWidth = byteSpacingWidth;
0348     } else {
0349         groupSpacingWidth = mByteArrayColumn->groupSpacingWidth();
0350     }
0351 
0352     const PixelX byteGroupWidth =  noOfGroupedBytes * byteWidth + (noOfGroupedBytes - 1) * byteSpacingWidth;
0353     const PixelX totalGroupWidth = byteGroupWidth + groupSpacingWidth;
0354 
0355     int fittingBytesPerLine;
0356     int fittingBytesPerLineWithScrollbar = 0;
0357     for (;;) {
0358         //    qCDebug(LOG_OKTETA_GUI) << "matchWidth: " << fullWidth
0359         //              << " (v:" << visibleWidth()
0360         //              << ", f:" << frameWidth()
0361         //              << ", A:" << availableWidth
0362         //              << ", S:" << scrollbarExtent
0363         //              << ", R:" << reservedWidth << ")" << endl;
0364 
0365         // calculate fitting groups per line
0366         const int fittingGroupsPerLine = (availableWidth + groupSpacingWidth) // fake spacing after last group
0367                                          / totalGroupWidth;
0368 
0369         // calculate the fitting bytes per line by groups
0370         fittingBytesPerLine = noOfGroupedBytes * fittingGroupsPerLine;
0371 
0372         // not only full groups?
0373         if (mResizeStyle == AbstractByteArrayView::FullSizeLayoutStyle && noOfGroupedBytes > 1) {
0374 
0375             if (fittingGroupsPerLine > 0) {
0376                 availableWidth -= fittingGroupsPerLine * totalGroupWidth; // includes additional spacing after last group
0377 
0378             }
0379 //         qCDebug(LOG_OKTETA_GUI) << "Left: " << availableWidth << "("<<byteWidth<<")" ;
0380 
0381             if (availableWidth > 0) {
0382                 fittingBytesPerLine += (availableWidth + byteSpacingWidth) / (byteWidth + byteSpacingWidth);
0383             }
0384 
0385             // is there not even the space for a single byte?
0386             if (fittingBytesPerLine == 0) {
0387                 // ensure at least one byte per line
0388                 fittingBytesPerLine = 1;
0389                 // and
0390                 break;
0391             }
0392         }
0393         // is there not the space for a single group?
0394         else if (fittingBytesPerLine == 0) {
0395             // ensures at least one group
0396             fittingBytesPerLine = noOfGroupedBytes;
0397             break;
0398         }
0399 
0400 //    qCDebug(LOG_OKTETA_GUI) << "meantime: " << fittingGroupsPerLine << " (T:" << totalGroupWidth
0401 //              << ", h:" << byteGroupWidth
0402 //              << ", s:" << groupSpacingWidth << ") " <<fittingBytesPerLine<< endl;
0403 
0404         const int newNoOfLines = (mTableLayout->length() + mTableLayout->startOffset() + fittingBytesPerLine - 1)
0405                                  / fittingBytesPerLine;
0406         const PixelY newHeight =  newNoOfLines * q->lineHeight();
0407 
0408         if (verticalScrollbarIsVisible) {
0409             if (matchRun == TestWithoutScrollbar) {
0410                 // did the test without the scrollbar fail, don't the data fit into the view?
0411                 if (newHeight > fullHeight) {
0412                     // reset to old calculated value
0413                     fittingBytesPerLine =  fittingBytesPerLineWithScrollbar;
0414                 }
0415                 break;
0416             }
0417 
0418             // a chance for to perhaps fit in height?
0419             if (fittingBytesPerLine <= mTableLayout->noOfBytesPerLine()) {
0420                 // remember this trial's result and calc number of bytes with vertical scrollbar on
0421                 fittingBytesPerLineWithScrollbar = fittingBytesPerLine;
0422                 availableWidth = fullWidth;
0423                 matchRun = TestWithoutScrollbar;
0424                 //          qCDebug(LOG_OKTETA_GUI) << "tested without scrollbar..." ;
0425                 continue;
0426             }
0427         } else {
0428             // doesn't it fit into the height anymore?
0429             if (newHeight > fullHeight && matchRun == FirstRun) {
0430                 // need for a scrollbar has risen... ->less width, new calculation
0431                 availableWidth = fullWidth - scrollbarExtent;
0432                 matchRun = RerunWithScrollbarOn;
0433                 //          qCDebug(LOG_OKTETA_GUI) << "rerun with scrollbar on..." ;
0434                 continue;
0435             }
0436         }
0437 
0438         break;
0439     }
0440 
0441     return fittingBytesPerLine;
0442 }
0443 
0444 void ByteArrayRowViewPrivate::setVisibleCodings(int newCodings)
0445 {
0446     Q_Q(ByteArrayRowView);
0447 
0448     const int oldCodings = visibleCodings();
0449 
0450     // no changes or no column selected?
0451     if (newCodings == oldCodings || !(newCodings & AbstractByteArrayView::ValueAndCharCodings)) {
0452         return;
0453     }
0454 
0455     mByteArrayColumn->setVisibleCodings(newCodings);
0456 
0457     // active coding not visible anymore?
0458     const bool isActiveStillVisible = (mActiveCoding & newCodings);
0459     if (!isActiveStillVisible) {
0460         mActiveCoding = (AbstractByteArrayView::CodingTypeId)newCodings;
0461         mInactiveCoding = AbstractByteArrayView::NoCodingId;
0462         adaptController();
0463     } else {
0464         mInactiveCoding = (AbstractByteArrayView::CodingTypeId)(newCodings ^ mActiveCoding);
0465     }
0466 
0467     const int rowHeight = mByteArrayColumn->rowHeight();
0468     q->setLineHeight(rowHeight);
0469 
0470     updateViewByWidth();
0471 
0472     Q_EMIT q->visibleByteArrayCodingsChanged(newCodings);
0473 }
0474 
0475 void ByteArrayRowViewPrivate::setActiveCoding(AbstractByteArrayView::CodingTypeId codingId)
0476 {
0477     // no changes or not visible?
0478     if (codingId == mActiveCoding) {
0479         return;
0480     }
0481 
0482     pauseCursor();
0483     mValueEditor->finishEdit();
0484 
0485     mActiveCoding = codingId;
0486     mInactiveCoding = (AbstractByteArrayView::CodingTypeId)(visibleCodings() ^ codingId);
0487 
0488     adaptController();
0489 
0490     ensureCursorVisible();
0491     unpauseCursor();
0492 }
0493 
0494 void ByteArrayRowViewPrivate::placeCursor(QPoint point)
0495 {
0496     Q_Q(ByteArrayRowView);
0497 
0498     const int lineIndex = q->lineAt(point.y());
0499     const PixelY lineY = lineIndex * q->lineHeight();
0500     const PixelY y = point.y() - lineY;
0501 
0502     const AbstractByteArrayView::CodingTypeId codingId = mByteArrayColumn->codingIdofY(y);
0503 
0504     // switch active column if needed
0505     if (codingId != mActiveCoding) {
0506         mActiveCoding = codingId;
0507         mInactiveCoding = (codingId != AbstractByteArrayView::ValueCodingId) ? AbstractByteArrayView::ValueCodingId :
0508                           AbstractByteArrayView::CharCodingId;
0509     }
0510 
0511     adaptController();
0512 
0513     // get coord of click and whether this click was closer to the end of the pos
0514     const int linePosition = mByteArrayColumn->magneticLinePositionOfX(point.x());
0515     const Coord coord(linePosition, lineIndex);
0516 
0517     mTableCursor->gotoCCoord(coord);
0518     Q_EMIT q->cursorPositionChanged(cursorPosition());
0519 }
0520 
0521 Address ByteArrayRowViewPrivate::indexByPoint(QPoint point) const
0522 {
0523     Q_Q(const ByteArrayRowView);
0524 
0525     const int lineIndex = q->lineAt(point.y());
0526     const int linePosition = mByteArrayColumn->linePositionOfX(point.x());
0527 
0528     const Coord coord(linePosition, lineIndex);
0529 
0530     return mTableLayout->indexAtCCoord(coord);
0531 }
0532 
0533 void ByteArrayRowViewPrivate::blinkCursor()
0534 {
0535     // skip the cursor drawing?
0536     if (mCursorPaused || mValueEditor->isInEditMode()) {
0537         return;
0538     }
0539 
0540     // switch the cursor state
0541     mBlinkCursorVisible = !mBlinkCursorVisible;
0542     updateCursor(*mByteArrayColumn, mActiveCoding);
0543 }
0544 
0545 void ByteArrayRowViewPrivate::updateCursors()
0546 {
0547     createCursorPixmaps();
0548 
0549     mBlinkCursorVisible = true;
0550     updateCursor(*mByteArrayColumn, mActiveCoding);
0551     updateCursor(*mByteArrayColumn, mInactiveCoding);
0552 }
0553 
0554 void ByteArrayRowViewPrivate::pauseCursor()
0555 {
0556     mCursorPaused = true;
0557 
0558     mBlinkCursorVisible = false;
0559     updateCursor(*mByteArrayColumn, mActiveCoding);
0560     updateCursor(*mByteArrayColumn, mInactiveCoding);
0561 }
0562 
0563 QRect ByteArrayRowViewPrivate::cursorRect() const
0564 {
0565     Q_Q(const ByteArrayRowView);
0566 
0567     QRect cursorRect = mByteArrayColumn->byteRect(mTableCursor->coord(), mActiveCoding);
0568     cursorRect.translate(-q->xOffset(), -q->yOffset());
0569 
0570     return cursorRect;
0571 }
0572 
0573 // TODO: should be movable to base class
0574 void ByteArrayRowViewPrivate::updateCursor(const ByteArrayRowColumnRenderer& column, AbstractByteArrayView::CodingTypeId codingId)
0575 {
0576     Q_Q(ByteArrayRowView);
0577 
0578     const bool isCodingVisible = (column.visibleCodings() & codingId);
0579     if (!isCodingVisible) {
0580         return;
0581     }
0582 
0583     QRect cursorRect = column.byteRect(mTableCursor->coord(), codingId);
0584     cursorRect.translate(-q->xOffset(), -q->yOffset());
0585 
0586     q->viewport()->update(cursorRect);
0587 }
0588 
0589 void ByteArrayRowViewPrivate::createCursorPixmaps()
0590 {
0591     Q_Q(ByteArrayRowView);
0592 
0593     const PixelX byteWidth = mByteArrayColumn->byteWidth();
0594 
0595     // create mCursorPixmaps
0596     mCursorPixmaps->setSize(byteWidth, mByteArrayColumn->digitHeight(), q->devicePixelRatio());
0597 
0598     const Address index = mTableCursor->validIndex();
0599 
0600     QPainter painter;
0601     painter.begin(&mCursorPixmaps->offPixmap());
0602     initPainterFromWidget(&painter);
0603     mByteArrayColumn->renderByte(&painter, index, mActiveCoding);
0604     painter.end();
0605 
0606     painter.begin(&mCursorPixmaps->onPixmap());
0607     initPainterFromWidget(&painter);
0608     mByteArrayColumn->renderCursor(&painter, index, mActiveCoding);
0609     painter.end();
0610 
0611     // calculate the shape
0612     PixelX cursorX;
0613     PixelX cursorW;
0614     if (isCursorBehind()) {
0615         cursorX = qMax(0, byteWidth - InsertCursorWidth);
0616         cursorW = InsertCursorWidth;
0617     } else {
0618         cursorX = 0;
0619         cursorW = mOverWrite ? -1 : InsertCursorWidth;
0620     }
0621     mCursorPixmaps->setShape(cursorX, cursorW, q->devicePixelRatio());
0622 }
0623 
0624 void ByteArrayRowViewPrivate::drawActiveCursor(QPainter* painter)
0625 {
0626     Q_Q(ByteArrayRowView);
0627 
0628     // any reason to skip the cursor drawing?
0629     if (!q->hasFocus() && !q->viewport()->hasFocus() && !mDropper->isActive()) {
0630         return;
0631     }
0632 
0633     const int x = mByteArrayColumn->xOfLinePosition(mTableCursor->pos());
0634     const int y = q->lineHeight() * mTableCursor->line()
0635                   + mByteArrayColumn->yOfCodingId(mActiveCoding);
0636 
0637     painter->translate(x, y);
0638 
0639     // paint edited byte?
0640     if (mValueEditor->isInEditMode()) {
0641         const Address index = mTableCursor->index();
0642 
0643         if (mBlinkCursorVisible) {
0644             mByteArrayColumn->renderEditedByte(painter, mValueEditor->value(), mValueEditor->valueAsString());
0645         } else {
0646             mByteArrayColumn->renderByte(painter, index, mActiveCoding);
0647         }
0648     } else {
0649         painter->drawPixmap(mCursorPixmaps->cursorX(), 0,
0650                             mBlinkCursorVisible ? mCursorPixmaps->onPixmap() : mCursorPixmaps->offPixmap(),
0651                             mCursorPixmaps->shapeX(), 0, mCursorPixmaps->shapeW(), -1);
0652     }
0653 
0654     painter->translate(-x, -y);
0655 }
0656 
0657 void ByteArrayRowViewPrivate::drawInactiveCursor(QPainter* painter)
0658 {
0659     Q_Q(ByteArrayRowView);
0660 
0661     // any reason to skip the cursor drawing?
0662     if (mInactiveCoding == AbstractByteArrayView::NoCodingId
0663         || mCursorPaused
0664         || (!mCursorPaused && !q->hasFocus() && !q->viewport()->hasFocus() && !mDropper->isActive())) {
0665         return;
0666     }
0667 
0668     const Address index = mTableCursor->validIndex();
0669 
0670     const int x = mByteArrayColumn->xOfLinePosition(mTableCursor->pos());
0671     const int y = q->lineHeight() * mTableCursor->line()
0672                   + mByteArrayColumn->yOfCodingId(mInactiveCoding);
0673     painter->translate(x, y);
0674 
0675     const ByteArrayRowColumnRenderer::FrameStyle frameStyle =
0676         mTableCursor->isBehind() ?                   ByteArrayRowColumnRenderer::Right :
0677         (mOverWrite || mValueEditor->isInEditMode()) ? ByteArrayRowColumnRenderer::Frame :
0678         ByteArrayRowColumnRenderer::Left;
0679     mByteArrayColumn->renderFramedByte(painter, index, mInactiveCoding, frameStyle);
0680 
0681     painter->translate(-x, -y);
0682 }
0683 
0684 void ByteArrayRowViewPrivate::renderColumns(QPainter* painter, int cx, int cy, int cw, int ch)
0685 {
0686     Q_Q(ByteArrayRowView);
0687 
0688     q->AbstractByteArrayView::renderColumns(painter, cx, cy, cw, ch);
0689     // TODO: update non blinking cursors. Should this perhaps be done in the buffercolumn?
0690     // Then it needs to know about inactive, insideByte and the like... well...
0691     // perhaps subclassing the buffer columns even more, to CharByteArrayColumnRenderer and ValueByteArrayColumnRenderer?
0692 
0693     if (q->visibleLines(PixelYRange::fromWidth(cy, ch)).includes(mTableCursor->line())) {
0694         drawActiveCursor(painter);
0695         drawInactiveCursor(painter);
0696     }
0697 }
0698 
0699 void ByteArrayRowViewPrivate::updateChanged()
0700 {
0701     Q_Q(ByteArrayRowView);
0702 
0703     const int xOffset = q->xOffset();
0704     const PixelXRange Xs = PixelXRange::fromWidth(xOffset, q->visibleWidth());
0705 
0706     // do updates in offset column
0707     const LineRange changedOffsetLines = mTableRanges->changedOffsetLines();
0708     if (!changedOffsetLines.isEmpty()) {
0709         q->updateColumn(*mOffsetColumn, changedOffsetLines);
0710     }
0711 
0712     // collect affected buffer columns
0713     QVector<ByteArrayRowColumnRenderer*> dirtyColumns;
0714 
0715     if (mByteArrayColumn->overlaps(Xs)) {
0716         dirtyColumns.append(mByteArrayColumn);
0717         mByteArrayColumn->prepareRendering(Xs);
0718     }
0719 
0720     // any columns to paint?
0721     if (!dirtyColumns.isEmpty()) {
0722         // calculate affected lines/indizes
0723         const LinePositionRange fullPositions(0, mTableLayout->noOfBytesPerLine() - 1);
0724         CoordRange visibleRange(fullPositions, q->visibleLines());
0725 
0726         const int lineHeight = q->lineHeight();
0727         CoordRange changedRange;
0728         // as there might be multiple selections on this line redo until no more is changed
0729         while (getNextChangedRange(&changedRange, visibleRange)) {
0730             PixelY cy = q->yOffsetOfLine(changedRange.start().line());
0731 
0732             // only one line?
0733             if (changedRange.start().line() == changedRange.end().line()) {
0734                 const LinePositionRange changedPositions(changedRange.start().pos(), changedRange.end().pos());
0735                 for (auto* column : std::as_const(dirtyColumns)) {
0736                     const PixelXRange xPixels = column->xsOfLinePositionsInclSpaces(changedPositions);
0737 
0738                     q->viewport()->update(xPixels.start() - xOffset, cy, xPixels.width(), lineHeight);
0739                 }
0740             }
0741             //
0742             else {
0743                 // first line
0744                 const LinePositionRange firstChangedPositions(changedRange.start().pos(), fullPositions.end());
0745                 for (auto* column : std::as_const(dirtyColumns)) {
0746                     const PixelXRange XPixels = column->xsOfLinePositionsInclSpaces(firstChangedPositions);
0747 
0748                     q->viewport()->update(XPixels.start() - xOffset, cy, XPixels.width(), lineHeight);
0749                 }
0750 
0751                 // at least one full line?
0752                 for (int l = changedRange.start().line() + 1; l < changedRange.end().line(); ++l) {
0753                     cy += lineHeight;
0754                     for (auto* column : std::as_const(dirtyColumns)) {
0755                         const PixelXRange XPixels = column->xsOfLinePositionsInclSpaces(fullPositions);
0756 
0757                         q->viewport()->update(XPixels.start() - xOffset, cy, XPixels.width(), lineHeight);
0758                     }
0759                 }
0760 
0761                 // last line
0762                 cy += lineHeight;
0763                 const LinePositionRange lastChangedPositions(fullPositions.start(), changedRange.end().pos());
0764                 for (auto* column : std::as_const(dirtyColumns)) {
0765                     const PixelXRange XPixels = column->xsOfLinePositionsInclSpaces(lastChangedPositions);
0766 
0767                     q->viewport()->update(XPixels.start() - xOffset, cy, XPixels.width(), lineHeight);
0768                 }
0769             }
0770 
0771             // continue the search at the overnext index
0772             visibleRange.setStart(changedRange.end() + 1); // +2 ); TODO: currently bounding ranges are not merged
0773             if (!visibleRange.isValid()) {
0774                 break;
0775             }
0776         }
0777     }
0778 
0779     mTableRanges->resetChangedRanges();
0780 }
0781 
0782 void ByteArrayRowViewPrivate::ensureCursorVisible()
0783 {
0784     ensureVisible(*mByteArrayColumn, mTableCursor->coord());
0785 }
0786 
0787 void ByteArrayRowViewPrivate::ensureVisible(const AddressRange& range, bool ensureStartVisible)
0788 {
0789     const CoordRange coords = mTableLayout->coordRangeOfIndizes(range);
0790 
0791     // TODO: this is a make-it-work-hack, better do a smart calculation
0792     ensureVisible(*mByteArrayColumn, ensureStartVisible ? coords.end() : coords.start());
0793     ensureVisible(*mByteArrayColumn, ensureStartVisible ? coords.start() : coords.end());
0794 }
0795 
0796 void ByteArrayRowViewPrivate::ensureVisible(const ByteArrayRowColumnRenderer& column, Coord coord)
0797 {
0798     Q_Q(ByteArrayRowView);
0799 
0800     const QRect byteRect = column.byteRect(coord);
0801 
0802     const PixelXRange byteXs = PixelXRange::fromWidth(byteRect.x(), byteRect.width());
0803     const PixelYRange byteYs = PixelYRange::fromWidth(byteRect.y(), byteRect.height());
0804 
0805     const PixelXRange visibleXs = PixelXRange::fromWidth(q->xOffset(), q->visibleWidth());
0806     const PixelYRange visibleYs = PixelXRange::fromWidth(q->yOffset(), q->visibleHeight());
0807 
0808     q->horizontalScrollBar()->setValue(visibleXs.startForInclude(byteXs));
0809     q->verticalScrollBar()->setValue(visibleYs.startForInclude(byteYs));
0810 }
0811 
0812 }