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

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