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

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