File indexing completed on 2024-04-14 15:52:53

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